From 5a94da8fe898b047fb271f4c2438f9a666f4fff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Sun, 28 Mar 2021 13:05:42 +0800 Subject: [PATCH 001/267] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..aff9c55d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# sample.dotblog +範例程式 From 0f870f873ca074edf089f0bd07faa33ac3256d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Sun, 28 Mar 2021 13:06:09 +0800 Subject: [PATCH 002/267] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aff9c55d..af9e31ca 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # sample.dotblog +https://dotblogs.com.tw/yc421206/ 範例程式 From 2a697aaafab53c39b4be0abae9e6398331118b7a Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 30 Mar 2021 15:05:22 +0800 Subject: [PATCH 003/267] add test case --- .../NetFx48/SurveyJsonConfigurationTests.cs | 97 +++++++++++++------ 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 6505ff81..62052544 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -10,28 +10,6 @@ namespace NetFx48 [TestClass] public class SurveyJsonConfigurationTests { - [TestMethod] - public void 記憶體組態() - { - var configBuilder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddInMemoryCollection(new Dictionary() - { - {"Player:AppId","player1"}, - {"Player:Key","1234567890"}, - {"ConnectionStrings:DefaultConnectionString","Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"}, - }) - ; - var configRoot = configBuilder.Build(); - - //讀取組態 - - Console.WriteLine($"AppId = {configRoot["AppId"]}"); - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); - } - [TestMethod] public void 切換組態() { @@ -50,12 +28,12 @@ public void 切換組態() .AddJsonFile("appsettings.json", false, true) .AddJsonFile($"appsettings.{environmentName}.json", true, true) ; - var configRoot = configBuilder.Build(); + var config = configBuilder.Build(); //讀取組態 - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + Console.WriteLine($"AppId = {config["Player:AppId"]}"); + Console.WriteLine($"Key = {config["Player:Key"]}"); + Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); } [TestMethod] @@ -65,14 +43,40 @@ public void 手動實例化ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", true, true) ; - var configRoot = configBuilder.Build(); + var config = configBuilder.Build(); + + //讀取組態 + + Console.WriteLine($"AppId = {config["AppId"]}"); + Console.WriteLine($"AppId = {config["Player:AppId"]}"); + Console.WriteLine($"Key = {config["Player:Key"]}"); + Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); + } + + [TestMethod] + public void 記憶體組態() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddInMemoryCollection(new Dictionary + { + {"Player:AppId", "player1"}, + {"Player:Key", "1234567890"}, + { + "ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var config = configBuilder.Build(); //讀取組態 - Console.WriteLine($"AppId = {configRoot["AppId"]}"); - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + Console.WriteLine($"AppId = {config["AppId"]}"); + Console.WriteLine($"AppId = {config["Player:AppId"]}"); + Console.WriteLine($"Key = {config["Player:Key"]}"); + Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); } [TestMethod] @@ -81,6 +85,20 @@ public void 通過Host() using var host = CreateHostBuilder(null).Build(); } + [TestMethod] + public void 讀取設定檔_GetSection() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + var config = builder.Build(); + + Console.WriteLine($"AppId = {config.GetSection("AppId")}"); + Console.WriteLine($"AppId = {config.GetSection("Player:AppId")}"); + Console.WriteLine($"Key = {config.GetSection("Player:Key")}"); + Console.WriteLine($"Connection String = {config.GetSection("ConnectionStrings:DefaultConnectionString")}"); + } + [TestMethod] public void 讀取設定檔_TryGet() { @@ -112,10 +130,25 @@ public void 讀取設定檔_綁定() Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); } + [TestMethod] + public void 讀取設定檔_綁定_Get() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + var config = builder.Build(); + var player = config.GetSection("Player").Get(); + var appSetting = config.Get(); + + Console.WriteLine($"AppId = {player.AppId}"); + Console.WriteLine($"Key = {appSetting.Player.Key}"); + Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); + } + private static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(( config) => + .ConfigureAppConfiguration(config => { config.Sources.Clear(); config.AddJsonFile("appsettings.json", true, true); From 03fb61f448236767d0f91da1b2ae139bf5793254 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 30 Mar 2021 15:47:01 +0800 Subject: [PATCH 004/267] di appservice --- .../NetCore/Lab.Config/NetFx48/AppService.cs | 19 ++++++++++++++++ .../NetFx48/{ => Models}/AppSetting.cs | 0 .../NetFx48/{ => Models}/ConnectionStrings.cs | 0 .../Lab.Config/NetFx48/{ => Models}/Player.cs | 0 .../NetFx48/SurveyJsonConfigurationTests.cs | 22 +++++++++++++++++++ 5 files changed, 41 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppService.cs rename Configuration/NetCore/Lab.Config/NetFx48/{ => Models}/AppSetting.cs (100%) rename Configuration/NetCore/Lab.Config/NetFx48/{ => Models}/ConnectionStrings.cs (100%) rename Configuration/NetCore/Lab.Config/NetFx48/{ => Models}/Player.cs (100%) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs new file mode 100644 index 00000000..f910244c --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; + +namespace NetFx48 +{ + public class AppService + { + private readonly IConfiguration _config; + + public AppService(IConfiguration config) + { + this._config = config; + } + + public string GetPlayerId() + { + return this._config.GetSection("Player:AppId").Value; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppSetting.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs similarity index 100% rename from Configuration/NetCore/Lab.Config/NetFx48/AppSetting.cs rename to Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/ConnectionStrings.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/ConnectionStrings.cs similarity index 100% rename from Configuration/NetCore/Lab.Config/NetFx48/ConnectionStrings.cs rename to Configuration/NetCore/Lab.Config/NetFx48/Models/ConnectionStrings.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/Player.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs similarity index 100% rename from Configuration/NetCore/Lab.Config/NetFx48/Player.cs rename to Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 62052544..26ef08b5 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -53,6 +54,27 @@ public void 手動實例化ConfigurationBuilder() Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); } + [TestMethod] + public void 注入Configuration() + { + var builder = Host.CreateDefaultBuilder(null) + .ConfigureAppConfiguration(config => + { + config.Sources.Clear(); + config.AddJsonFile("appsettings.json", true, true); + }) + .ConfigureServices(service => + { + //DI + service.AddScoped(typeof(AppService)); + }); + var host = builder.Build(); + + var appService = host.Services.GetService(); + var playerId = appService.GetPlayerId(); + Console.WriteLine($"AppId = {playerId}"); + } + [TestMethod] public void 記憶體組態() { From 4ba23311bee57a269b62fbc59c380d421de90da1 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 30 Mar 2021 15:52:56 +0800 Subject: [PATCH 005/267] refactor --- Configuration/NetCore/Lab.Config/NetFx48/AppService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs index f910244c..40388117 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs @@ -2,7 +2,12 @@ namespace NetFx48 { - public class AppService + public interface IAppService + { + string GetPlayerId(); + } + + public class AppService : IAppService { private readonly IConfiguration _config; From c6496651fbde182a9edd0212c532a69bcf774af4 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 30 Mar 2021 17:20:35 +0800 Subject: [PATCH 006/267] =?UTF-8?q?add=20=E5=AF=A6=E4=BE=8B=E5=8C=96JsonCo?= =?UTF-8?q?nfigurationProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NetFx48/SurveyJsonConfigurationTests.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 26ef08b5..8fdcb1e1 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -16,11 +17,11 @@ public void 切換組態() { string environmentName; #if DEBUG - environmentName = "Development"; + environmentName = "Development"; #elif QA - environmentName = "QA"; + environmentName = "QA"; #elif STAGING - environmentName = "Staging"; + environmentName = "Staging"; #elif RELEASE environmentName = "Production"; #endif @@ -107,6 +108,20 @@ public void 通過Host() using var host = CreateHostBuilder(null).Build(); } + [TestMethod] + public void 實例化JsonConfigurationProvider() + { + var configProvider = new JsonConfigurationProvider(new JsonConfigurationSource + { + Optional = false, + Path = "appsettings.json", + ReloadOnChange = true + }); + configProvider.Load(); + configProvider.TryGet("Player:AppId", out var appId); + Console.WriteLine($"AppId = {appId}"); + } + [TestMethod] public void 讀取設定檔_GetSection() { From eff10703ee5951b3844d87e4f209a09618bc82ac Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 30 Mar 2021 18:13:27 +0800 Subject: [PATCH 007/267] add SurveyCommandConfigurationTests --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 1 + .../SurveyCommandConfigurationTests.cs | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index 693d138f..21e71a51 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -26,6 +26,7 @@ + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs new file mode 100644 index 00000000..1ed04512 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration.CommandLine; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyCommandConfigurationTests + { + [TestMethod] + public void 命令對應() + { + string[] args = {"-i=1234567890", "-c=app.json"}; + + var map = new Dictionary + { + {"-i", "--AppId"}, + {"-c", "--Config"} + }; + + var provider = new CommandLineConfigurationProvider(args, map); + provider.Load(); + + provider.TryGet("AppId", out var appId); + Console.WriteLine($"{args.First()}\r\n" + + $"AppId:{appId}"); + } + + [TestMethod] + public void 傳參數給應用程式_使用Host() + { + // string[] args = {"/appId 1234567890"}; + string[] args = {"/AppId=1234567890"}; + var builder = Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(config => + { + // config.Sources.Clear(); + // config.AddJsonFile("appsettings.json", true, true); + // config.AddCommandLine(args); + var configRoot = config.Build(); + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + }) + .ConfigureServices(service => + { + //DI + service.AddScoped(typeof(AppService)); + }); + var host = builder.Build(); + } + + [TestMethod] + [DataRow(new[] {"--AppId=1234567890"})] + [DataRow(new[] {"/AppId=1234567890"})] + [DataRow(new[] {"AppId=1234567890"})] + public void 實例化CommandLineConfigurationProvider(string[] args) + { + var provider = new CommandLineConfigurationProvider(args); + provider.Load(); + provider.TryGet("AppId", out var appId); + Console.WriteLine($"{args.First()}\r\n" + + $"AppId:{appId}"); + } + } +} \ No newline at end of file From a41d2714d91a6de46d6d1e1700ee5c9aa30b7387 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 31 Mar 2021 09:26:19 +0800 Subject: [PATCH 008/267] refactor --- .../SurveyCommandConfigurationTests.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs index 1ed04512..7c00b012 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.CommandLine; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -15,40 +16,51 @@ public class SurveyCommandConfigurationTests public void 命令對應() { string[] args = {"-i=1234567890", "-c=app.json"}; - + var map = new Dictionary { - {"-i", "--AppId"}, - {"-c", "--Config"} + {"-i", "AppId"}, + {"-c", "Config"} }; - + var provider = new CommandLineConfigurationProvider(args, map); provider.Load(); - + provider.TryGet("AppId", out var appId); + provider.TryGet("Config", out var configPath); Console.WriteLine($"{args.First()}\r\n" + - $"AppId:{appId}"); + $"AppId:{appId}\r\n" + + $"ConfigPath:{configPath}"); } [TestMethod] - public void 傳參數給應用程式_使用Host() + [DataRow(new[] {"-i=1234567890", "-c=app.json"})] + public void 命令對應_Host(string[] args) { - // string[] args = {"/appId 1234567890"}; - string[] args = {"/AppId=1234567890"}; - var builder = Host.CreateDefaultBuilder(args) + var map = new Dictionary + { + {"-i", "AppId"}, + {"-c", "Config"} + }; + var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration(config => { // config.Sources.Clear(); - // config.AddJsonFile("appsettings.json", true, true); - // config.AddCommandLine(args); + config.AddCommandLine(args, map); var configRoot = config.Build(); - Console.WriteLine($"AppId = {configRoot["AppId"]}"); + + var appId = configRoot["AppId"]; + var configPath = configRoot["Config"]; + Console.WriteLine($"{args.First()}\r\n" + + $"AppId:{appId}\r\n" + + $"ConfigPath:{configPath}"); }) .ConfigureServices(service => { //DI service.AddScoped(typeof(AppService)); - }); + }) + ; var host = builder.Build(); } From c069b373210a97d1fc4e1de17dc9d684fa5a1fc1 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 31 Mar 2021 12:10:52 +0800 Subject: [PATCH 009/267] add SurveyKeyPerFileConfigurationTests --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 4 +++ .../SurveyKeyPerFileConfigurationTests.cs | 27 +++++++++++++++++++ .../NetFx48/keys/aws/web/NewFile1.txt | 1 + 3 files changed, 32 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/keys/aws/web/NewFile1.txt diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index 21e71a51..f36333af 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -27,6 +27,7 @@ + @@ -48,6 +49,9 @@ Always + + Always + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs new file mode 100644 index 00000000..a0cb8de4 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyKeyPerFileConfigurationTests + { + [TestMethod] + public void 手動實例化ConfigurationBuilder() + { + var expected = "我是檔案內容"; + var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "keys/aws/web"); + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddKeyPerFile(folderPath,false); + var config = configBuilder.Build(); + + //讀取組態 + var actual = config["NewFile1.txt"]; + Console.WriteLine($"NewFile1.txt = {actual}"); + Assert.AreEqual(expected,actual); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/keys/aws/web/NewFile1.txt b/Configuration/NetCore/Lab.Config/NetFx48/keys/aws/web/NewFile1.txt new file mode 100644 index 00000000..8c7cec77 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/keys/aws/web/NewFile1.txt @@ -0,0 +1 @@ +我是檔案內容 \ No newline at end of file From ba29ae626f4bb3c64a0634880bf0346bef6f91da Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 31 Mar 2021 17:34:52 +0800 Subject: [PATCH 010/267] =?UTF-8?q?=E7=92=B0=E5=A2=83=E8=AE=8A=E6=95=B8?= =?UTF-8?q?=E5=88=87=E6=8F=9B=E7=B5=84=E6=85=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 5 ++ ...yEnvironmentVariablesConfigurationTests.cs | 73 +++++++++++++++++++ .../Lab.Config/NetFx48/appsettings.test.json | 10 +++ 3 files changed, 88 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index f36333af..594af989 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -37,6 +37,11 @@ Always PreserveNewest + + true + Always + PreserveNewest + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs new file mode 100644 index 00000000..53043e3f --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs @@ -0,0 +1,73 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyEnvironmentVariablesConfigurationTests + { + [TestMethod] + public void Host實例化ConfigurationBuilder() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // config.Sources.Clear(); + + configBuilder.AddEnvironmentVariables("Custom_"); + var configRoot = configBuilder.Build(); + + //讀取組態 + Console + .WriteLine($"ASPNETCORE_ENVIRONMENT = {configRoot["ASPNETCORE_ENVIRONMENT"]}"); + Console + .WriteLine($"DOTNET_ENVIRONMENT2 = {configRoot["DOTNET_ENVIRONMENT2"]}"); + Console + .WriteLine($"CUSTOM_ENVIRONMENT1 = {configRoot["CUSTOM_ENVIRONMENT1"]}"); + Console + .WriteLine($"ENVIRONMENT1 = {configRoot["ENVIRONMENT1"]}"); + }) + ; + builder.Build(); + } + + [TestMethod] + public void 切換組態設定() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // config.Sources.Clear(); + var environmentName = hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json",false,true); + configBuilder.AddJsonFile($"appsettings.{environmentName}.json",true,true); + + var configRoot = configBuilder.Build(); + + //讀取組態 + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + }) + ; + builder.Build(); + } + + [TestMethod] + public void 手動實例化ConfigurationBuilder() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddEnvironmentVariables("ASPNETCORE_") + ; + + var configRoot = configBuilder.Build(); + + //讀取組態 + Console.WriteLine($"ENVIRONMENT = {configRoot["ENVIRONMENT"]}"); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json new file mode 100644 index 00000000..c339236f --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json @@ -0,0 +1,10 @@ +{ + "ConnectionStrings": { + "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb.Test;Trusted_Connection=True;" + }, + + "Player": { + "AppId": "test", + "Key": "test1234567890" + } +} \ No newline at end of file From 7ac2e8481d1e394baf607ed0338028ee3d71dd3b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 31 Mar 2021 19:24:01 +0800 Subject: [PATCH 011/267] add miss json file --- .../Lab.Config/AspNetCore3/appsettings.json | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json new file mode 100644 index 00000000..6159d462 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -0,0 +1,27 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "ConnectionStrings": { + "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + "Player": { + "AppId": "test", + "Key": "12345678990" + }, + + "Player1": { + "AppId": "testApp", + "Key": "12345678990" + }, + "Player2": { + "AppId": "testApp", + "Key": "12345678990" + } +} \ No newline at end of file From 1ef79b4c4ab19d1b4f2f9547d787fb52a4c139cd Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 31 Mar 2021 23:39:09 +0800 Subject: [PATCH 012/267] add options pattern sample --- .../NetCore/Lab.Config/NetFx48/AppService.cs | 23 ++++++++++ .../Lab.Config/NetFx48/Models/AppSetting.cs | 8 ++++ .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 1 + .../Lab.Config/NetFx48/SurveyOptionTests.cs | 42 +++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs index 40388117..5e1f91dc 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; namespace NetFx48 { @@ -21,4 +22,26 @@ public string GetPlayerId() return this._config.GetSection("Player:AppId").Value; } } + + public class AppServiceWithOption : IAppService + { + private readonly AppSetting1 _appSetting; + + public AppServiceWithOption(IOptions options) + { + this._appSetting = options.Value; + } + public AppServiceWithOption(IOptionsSnapshot options) + { + this._appSetting = options.Value; + } + public AppServiceWithOption(IOptionsMonitor options) + { + this._appSetting = options.CurrentValue; + } + public string GetPlayerId() + { + return this._appSetting.Player.AppId; + } + } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs index fb48e8d3..21ad3ba8 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs @@ -6,4 +6,12 @@ public struct AppSetting public ConnectionStrings ConnectionStrings { get; set; } } + + public class AppSetting1 + { + public Player Player { get; set; } + + public ConnectionStrings ConnectionStrings { get; set; } + } + } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index 594af989..679a98ac 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -28,6 +28,7 @@ + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs new file mode 100644 index 00000000..8d0c27c2 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyOptionTests + { + [TestMethod] + public void `JOption() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // 1.ŪպA + var environmentName = hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json",false,true); + configBuilder.AddJsonFile($"appsettings.{environmentName}.json",true,true); + }) + .ConfigureServices((hosting, services) => + { + // 2.`J Options + services.AddOptions(); + // 3. `J IConfiguration + services.Configure(hosting.Configuration); + + //`JLA + services.AddSingleton(); + }) + ; + var host = builder.Build(); + var service = host.Services.GetService(); + var playerId = service.GetPlayerId(); + Console.WriteLine($"PlayerId={playerId}"); + } + } +} \ No newline at end of file From 26d2b857fd4487a1747ba95c7407cf641d555d63 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 10:13:29 +0800 Subject: [PATCH 013/267] refactor --- .../Controllers/DefaultController.cs | 25 +++++++ .../Controllers/WeatherForecastController.cs | 48 +++++++------ .../AspNetCore3/ServiceCollectionEx.cs | 70 +++++++++---------- .../NetCore/Lab.Config/NetFx48/AppService.cs | 9 +-- 4 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs new file mode 100644 index 00000000..1a685ecc --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -0,0 +1,25 @@ +using Lab.Infra; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace AspNetCore3.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + public IActionResult Get() + { + var setting = this.GetAppSetting(); + return this.Ok(setting); + } + + private AppSetting GetAppSetting() + { + var serviceProvider = this.HttpContext.RequestServices; + var options = serviceProvider.GetService>(); + return options?.Value; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs index 2fb9663f..4fc05dd9 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs @@ -25,26 +25,26 @@ public class WeatherForecastController : ControllerBase private Player _player2; // TODO:依賴 AppSetting - public WeatherForecastController(AppSetting appSetting) - { - this._appSetting = appSetting; - } + // public WeatherForecastController(AppSetting appSetting) + // { + // this._appSetting = appSetting; + // } // TODO:依賴 IOptions - //public WeatherForecastController(IOptions options) - //{ - // try - // { - // this._appSetting = options.Value; - // } - // catch (OptionsValidationException ex) - // { - // foreach (var failure in ex.Failures) - // { - // Console.WriteLine(failure); - // } - // } - //} + public WeatherForecastController(IOptions options) + { + try + { + this._appSetting = options.Value; + } + catch (OptionsValidationException ex) + { + foreach (var failure in ex.Failures) + { + Console.WriteLine(failure); + } + } + } // TODO:依賴 IOptionsSnapshot //public WeatherForecastController(IOptionsSnapshot options) @@ -66,11 +66,13 @@ public WeatherForecastController(AppSetting appSetting) // this._config = config; //} - //public WeatherForecastController(IOptions options, IConfiguration config) - //{ - // this._config = config; - // this._appSetting = options.Value; - //} + // public WeatherForecastController(IOptions options, IConfiguration config) + // { + // this._config = config; + // var appSetting = new AppSetting(); + // config.Bind(appSetting); + // this._appSetting = options.Value; + // } //public WeatherForecastController(ILogger logger) //{ diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs index 1134ef0e..907ebd43 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs @@ -1,35 +1,35 @@ -using System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace AspNetCore3 -{ - public static class ServiceCollectionEx - { - /// - /// Inject AddSingleton - /// - /// - /// - /// - /// - public static TConfig Configure(this IServiceCollection services, IConfiguration configuration) - where TConfig : class, new() - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - var config = Activator.CreateInstance(); - configuration.Bind(config); - services.AddSingleton(config); - return config; - } - } -} \ No newline at end of file +// using System; +// using Microsoft.Extensions.Configuration; +// using Microsoft.Extensions.DependencyInjection; +// +// namespace AspNetCore3 +// { +// public static class ServiceCollectionEx +// { +// /// +// /// Inject AddSingleton +// /// +// /// +// /// +// /// +// /// +// public static TConfig Configure(this IServiceCollection services, IConfiguration configuration) +// where TConfig : class, new() +// { +// if (services == null) +// { +// throw new ArgumentNullException(nameof(services)); +// } +// +// if (configuration == null) +// { +// throw new ArgumentNullException(nameof(configuration)); +// } +// +// var config = Activator.CreateInstance(); +// configuration.Bind(config); +// services.AddSingleton(config); +// return config; +// } +// } +// } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs index 5e1f91dc..7bf16e6f 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs @@ -31,14 +31,7 @@ public AppServiceWithOption(IOptions options) { this._appSetting = options.Value; } - public AppServiceWithOption(IOptionsSnapshot options) - { - this._appSetting = options.Value; - } - public AppServiceWithOption(IOptionsMonitor options) - { - this._appSetting = options.CurrentValue; - } + public string GetPlayerId() { return this._appSetting.Player.AppId; From bd1e032ebe46684e3bba842e3114a6705b6ed550 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 11:17:08 +0800 Subject: [PATCH 014/267] refactor --- .../Controllers/DefaultController.cs | 20 +++++++- .../NetCore/Lab.Config/AspNetCore3/Program.cs | 9 ++-- .../Properties/launchSettings.json | 3 +- ...tting.cs => StartupInjectionAppSetting.cs} | 4 +- .../StartupInjectionIOptionsMonitor.cs | 51 +++++++++++++++++++ ...IOptions.cs => StartupInjectionOptions.cs} | 4 +- ....cs => StartupInjectionOptionsSnapshot.cs} | 4 +- .../Lab.Config/AspNetCore3/appsettings.json | 8 +-- 8 files changed, 87 insertions(+), 16 deletions(-) rename Configuration/NetCore/Lab.Config/AspNetCore3/{Startup_InjectionIAppSetting.cs => StartupInjectionAppSetting.cs} (91%) create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs rename Configuration/NetCore/Lab.Config/AspNetCore3/{Startup_InjectionIOptions.cs => StartupInjectionOptions.cs} (91%) rename Configuration/NetCore/Lab.Config/AspNetCore3/{Startup_InjectionIOptionsSnapshot.cs => StartupInjectionOptionsSnapshot.cs} (93%) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs index 1a685ecc..612b0b05 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -1,3 +1,4 @@ +using System; using Lab.Infra; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; @@ -11,7 +12,8 @@ public class DefaultController : ControllerBase { public IActionResult Get() { - var setting = this.GetAppSetting(); + // var setting = this.GetAppSetting(); + var setting = this.GetAppSettingMonitor(); return this.Ok(setting); } @@ -21,5 +23,21 @@ private AppSetting GetAppSetting() var options = serviceProvider.GetService>(); return options?.Value; } + + private AppSetting GetAppSettingMonitor() + { + var serviceProvider = this.HttpContext.RequestServices; + var appsSettingOptions = serviceProvider.GetService>(); + var playerOption = serviceProvider.GetService>(); + var player1 = playerOption.Get("Player1"); + var player2 = playerOption.Get("Player2"); + Console.WriteLine($"player1={player1.AppId}"); + Console.WriteLine($"player2={player2.AppId}"); + + // var appSetting = options.Get("Player1"); + // return options?.CurrentValue; + + return null; + } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs index c9edcdf5..a78569e1 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs @@ -20,10 +20,11 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { - //webBuilder.UseStartup(); - //webBuilder.UseStartup(); - //webBuilder.UseStartup(); - webBuilder.UseStartup(); + // webBuilder.UseStartup(); + // webBuilder.UseStartup(); + // webBuilder.UseStartup(); + webBuilder.UseStartup(); + // webBuilder.UseStartup(); }); } } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json index 886261e3..649d735f 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json @@ -12,7 +12,8 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "weatherforecast", + "//launchUrl": "weatherforecast", + "launchUrl": "default", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIAppSetting.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs similarity index 91% rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIAppSetting.cs rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs index 59fd68f8..877559e9 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIAppSetting.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs @@ -7,11 +7,11 @@ namespace AspNetCore3 { - public class Startup_InjectionIAppSetting + public class StartupInjectionAppSetting { public IConfiguration Configuration { get; } - public Startup_InjectionIAppSetting(IConfiguration configuration) + public StartupInjectionAppSetting(IConfiguration configuration) { this.Configuration = configuration; } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs new file mode 100644 index 00000000..c14cd603 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs @@ -0,0 +1,51 @@ +using Lab.Infra; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace AspNetCore3 +{ + public class StartupInjectionOptionsMonitor + { + public IConfiguration Configuration { get; } + + public StartupInjectionOptionsMonitor(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + //`J IOptions + services.AddOptions(); + + //`J IConfiguration + services.Configure(this.Configuration); + services.Configure("Player", this.Configuration.GetSection("Player")); + services.Configure("Player1", this.Configuration.GetSection("Player1")); + services.Configure("Player2", this.Configuration.GetSection("Player2")); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptions.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs similarity index 91% rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptions.cs rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs index 716c5e74..70ec5f2d 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptions.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs @@ -7,11 +7,11 @@ namespace AspNetCore3 { - public class Startup_InjectionIOptions + public class StartupInjectionOptions { public IConfiguration Configuration { get; } - public Startup_InjectionIOptions(IConfiguration configuration) + public StartupInjectionOptions(IConfiguration configuration) { this.Configuration = configuration; } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs similarity index 93% rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptionsSnapshot.cs rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs index ee727426..3b5933bd 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptionsSnapshot.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs @@ -7,11 +7,11 @@ namespace AspNetCore3 { - public class Startup_InjectionIOptionsSnapshot + public class StartupInjectionOptionsSnapshot { public IConfiguration Configuration { get; } - public Startup_InjectionIOptionsSnapshot(IConfiguration configuration) + public StartupInjectionOptionsSnapshot(IConfiguration configuration) { this.Configuration = configuration; } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json index 6159d462..8ee8b905 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -17,11 +17,11 @@ }, "Player1": { - "AppId": "testApp", - "Key": "12345678990" + "AppId": "player1", + "Key": "player1_123456" }, "Player2": { - "AppId": "testApp", - "Key": "12345678990" + "AppId": "player2", + "Key": "player2_123456" } } \ No newline at end of file From fdf1d405ba23a5048d54d2adea7c548389f7161f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 11:52:13 +0800 Subject: [PATCH 015/267] refactor --- .../NetFx48/SurveyJsonConfigurationTests.cs | 70 ++++++++++++------- .../SurveyKeyPerFileConfigurationTests.cs | 4 +- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 8fdcb1e1..537cba32 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -30,12 +30,12 @@ public void 切換組態() .AddJsonFile("appsettings.json", false, true) .AddJsonFile($"appsettings.{environmentName}.json", true, true) ; - var config = configBuilder.Build(); + var configRoot = configBuilder.Build(); //讀取組態 - Console.WriteLine($"AppId = {config["Player:AppId"]}"); - Console.WriteLine($"Key = {config["Player:Key"]}"); - Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); } [TestMethod] @@ -45,14 +45,14 @@ public void 手動實例化ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", true, true) ; - var config = configBuilder.Build(); + var configRoot = configBuilder.Build(); //讀取組態 - Console.WriteLine($"AppId = {config["AppId"]}"); - Console.WriteLine($"AppId = {config["Player:AppId"]}"); - Console.WriteLine($"Key = {config["Player:Key"]}"); - Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); } [TestMethod] @@ -92,14 +92,14 @@ public void 記憶體組態() }) ; - var config = configBuilder.Build(); + var configRoot = configBuilder.Build(); //讀取組態 - Console.WriteLine($"AppId = {config["AppId"]}"); - Console.WriteLine($"AppId = {config["Player:AppId"]}"); - Console.WriteLine($"Key = {config["Player:Key"]}"); - Console.WriteLine($"Connection String = {config["ConnectionStrings:DefaultConnectionString"]}"); + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); } [TestMethod] @@ -128,12 +128,30 @@ public void 讀取設定檔_GetSection() var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var config = builder.Build(); + var configRoot = builder.Build(); - Console.WriteLine($"AppId = {config.GetSection("AppId")}"); - Console.WriteLine($"AppId = {config.GetSection("Player:AppId")}"); - Console.WriteLine($"Key = {config.GetSection("Player:Key")}"); - Console.WriteLine($"Connection String = {config.GetSection("ConnectionStrings:DefaultConnectionString")}"); + Console.WriteLine($"AppId = {configRoot.GetSection("AppId")}"); + Console.WriteLine($"AppId = {configRoot.GetSection("Player:AppId")}"); + Console.WriteLine($"Key = {configRoot.GetSection("Player:Key")}"); + Console.WriteLine($"Connection String = {configRoot.GetSection("ConnectionStrings:DefaultConnectionString")}"); + } + + [TestMethod] + public void 讀取設定檔_GetChild() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + var configRoot = builder.Build(); + var firstSections = configRoot.GetChildren(); + foreach (var firstSection in firstSections) + { + var secondSections = firstSection.GetChildren(); + foreach (var secondSection in secondSections) + { + Console.WriteLine($"{secondSection.Key}={secondSection.Value}\tPath={secondSection.Path}"); + } + } } [TestMethod] @@ -142,10 +160,10 @@ public void 讀取設定檔_TryGet() var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var config = builder.Build(); + var configRoot = builder.Build(); //TryGet - foreach (var provider in config.Providers) + foreach (var provider in configRoot.Providers) { provider.TryGet("Player:AppId", out var value); Console.WriteLine($"AppId = {value}"); @@ -158,10 +176,10 @@ public void 讀取設定檔_綁定() var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var config = builder.Build(); + var configRoot = builder.Build(); var appSetting = new AppSetting(); - config.Bind(appSetting); + configRoot.Bind(appSetting); Console.WriteLine($"AppId = {appSetting.Player.AppId}"); Console.WriteLine($"Key = {appSetting.Player.Key}"); Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); @@ -173,9 +191,9 @@ public void 讀取設定檔_綁定_Get() var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var config = builder.Build(); - var player = config.GetSection("Player").Get(); - var appSetting = config.Get(); + var configRoot = builder.Build(); + var player = configRoot.GetSection("Player").Get(); + var appSetting = configRoot.Get(); Console.WriteLine($"AppId = {player.AppId}"); Console.WriteLine($"Key = {appSetting.Player.Key}"); diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs index a0cb8de4..5205b0f4 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs @@ -16,10 +16,10 @@ public void 手動實例化ConfigurationBuilder() var configBuilder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddKeyPerFile(folderPath,false); - var config = configBuilder.Build(); + var configRoot = configBuilder.Build(); //讀取組態 - var actual = config["NewFile1.txt"]; + var actual = configRoot["NewFile1.txt"]; Console.WriteLine($"NewFile1.txt = {actual}"); Assert.AreEqual(expected,actual); } From 5afb35a710048c1b5cda0031a0c769384a84cf10 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 13:39:04 +0800 Subject: [PATCH 016/267] refactor --- .../Controllers/DefaultController.cs | 15 +- .../Controllers/WeatherForecastController.cs | 190 +++++++++--------- .../StartupInjectionIOptionsMonitor.cs | 4 +- .../Lab.Config/AspNetCore3/appsettings.json | 12 +- .../NetFx48/AppServiceWithOptionsMonitor.cs | 26 +++ .../NetFx48/AppServiceWithOptionsSnapshot.cs | 26 +++ .../Lab.Config/NetFx48/Models/Player.cs | 7 + .../Lab.Config/NetFx48/SurveyOptionTests.cs | 81 +++++++- 8 files changed, 248 insertions(+), 113 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs index 612b0b05..48fe7d64 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -10,6 +10,8 @@ namespace AspNetCore3.Controllers [Route("[controller]")] public class DefaultController : ControllerBase { + // [Route("controller/appsettings")] + [Route("appsettings")] public IActionResult Get() { // var setting = this.GetAppSetting(); @@ -17,6 +19,15 @@ public IActionResult Get() return this.Ok(setting); } + [Route("players/{id}")] + public IActionResult GetPlayer(int id) + { + var serviceProvider = this.HttpContext.RequestServices; + var playerOption = serviceProvider.GetService>(); + var player = playerOption.Get($"Player{id}"); + return this.Ok(player); + } + private AppSetting GetAppSetting() { var serviceProvider = this.HttpContext.RequestServices; @@ -35,9 +46,7 @@ private AppSetting GetAppSettingMonitor() Console.WriteLine($"player2={player2.AppId}"); // var appSetting = options.Get("Player1"); - // return options?.CurrentValue; - - return null; + return appsSettingOptions?.CurrentValue; } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs index 4fc05dd9..f50947bf 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs @@ -1,95 +1,95 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Lab.Infra; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace AspNetCore3.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - private AppSetting _appSetting; - private IConfiguration _config; - private Player _player1; - private Player _player2; - - // TODO:依賴 AppSetting - // public WeatherForecastController(AppSetting appSetting) - // { - // this._appSetting = appSetting; - // } - - // TODO:依賴 IOptions - public WeatherForecastController(IOptions options) - { - try - { - this._appSetting = options.Value; - } - catch (OptionsValidationException ex) - { - foreach (var failure in ex.Failures) - { - Console.WriteLine(failure); - } - } - } - - // TODO:依賴 IOptionsSnapshot - //public WeatherForecastController(IOptionsSnapshot options) - //{ - // this._player1 = options.Get("Player1"); - // this._player2 = options.Get("Player2"); - //} - - //// TODO:依賴 IOptionsMonitor - //public WeatherForecastController(IOptionsMonitor options) - //{ - // this._player1 = options.Get("Player1"); - // this._player2 = options.Get("Player2"); - //} - - // TODO:依賴 IConfiguration - //public WeatherForecastController(IConfiguration config) - //{ - // this._config = config; - //} - - // public WeatherForecastController(IOptions options, IConfiguration config) - // { - // this._config = config; - // var appSetting = new AppSetting(); - // config.Bind(appSetting); - // this._appSetting = options.Value; - // } - - //public WeatherForecastController(ILogger logger) - //{ - // _logger = logger; - //} - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} \ No newline at end of file +// using System; +// using System.Collections.Generic; +// using System.Linq; +// using Lab.Infra; +// using Microsoft.AspNetCore.Mvc; +// using Microsoft.Extensions.Configuration; +// using Microsoft.Extensions.Logging; +// using Microsoft.Extensions.Options; +// +// namespace AspNetCore3.Controllers +// { +// [ApiController] +// [Route("[controller]")] +// public class WeatherForecastController : ControllerBase +// { +// private static readonly string[] Summaries = +// { +// "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +// }; +// +// private readonly ILogger _logger; +// private AppSetting _appSetting; +// private IConfiguration _config; +// private Player _player1; +// private Player _player2; +// +// // TODO:依賴 AppSetting +// // public WeatherForecastController(AppSetting appSetting) +// // { +// // this._appSetting = appSetting; +// // } +// +// // TODO:依賴 IOptions +// public WeatherForecastController(IOptions options) +// { +// try +// { +// this._appSetting = options.Value; +// } +// catch (OptionsValidationException ex) +// { +// foreach (var failure in ex.Failures) +// { +// Console.WriteLine(failure); +// } +// } +// } +// +// // TODO:依賴 IOptionsSnapshot +// //public WeatherForecastController(IOptionsSnapshot options) +// //{ +// // this._player1 = options.Get("Player1"); +// // this._player2 = options.Get("Player2"); +// //} +// +// //// TODO:依賴 IOptionsMonitor +// //public WeatherForecastController(IOptionsMonitor options) +// //{ +// // this._player1 = options.Get("Player1"); +// // this._player2 = options.Get("Player2"); +// //} +// +// // TODO:依賴 IConfiguration +// //public WeatherForecastController(IConfiguration config) +// //{ +// // this._config = config; +// //} +// +// // public WeatherForecastController(IOptions options, IConfiguration config) +// // { +// // this._config = config; +// // var appSetting = new AppSetting(); +// // config.Bind(appSetting); +// // this._appSetting = options.Value; +// // } +// +// //public WeatherForecastController(ILogger logger) +// //{ +// // _logger = logger; +// //} +// +// [HttpGet] +// public IEnumerable Get() +// { +// var rng = new Random(); +// return Enumerable.Range(1, 5).Select(index => new WeatherForecast +// { +// Date = DateTime.Now.AddDays(index), +// TemperatureC = rng.Next(-20, 55), +// Summary = Summaries[rng.Next(Summaries.Length)] +// }) +// .ToArray(); +// } +// } +// } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs index c14cd603..cd938f30 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs @@ -43,9 +43,9 @@ public void ConfigureServices(IServiceCollection services) //`J IConfiguration services.Configure(this.Configuration); - services.Configure("Player", this.Configuration.GetSection("Player")); - services.Configure("Player1", this.Configuration.GetSection("Player1")); + services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); + services.Configure("Player3", this.Configuration.GetSection("Player3")); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json index 8ee8b905..b00bf6b0 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -11,17 +11,17 @@ "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" }, - "Player": { - "AppId": "test", - "Key": "12345678990" - }, - "Player1": { "AppId": "player1", - "Key": "player1_123456" + "Key": "12345678990" }, + "Player2": { "AppId": "player2", "Key": "player2_123456" + }, + "Player3": { + "AppId": "player3", + "Key": "player3_123456" } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs new file mode 100644 index 00000000..90a837ff --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.Options; + +namespace NetFx48 +{ + public class AppServiceWithOptionsMonitor : IAppService + { + private readonly AppSetting1 _appSetting; + private readonly Player1 _player; + + public AppServiceWithOptionsMonitor(IOptionsMonitor appSettingOption, + IOptionsMonitor playerOption) + { + this._player = playerOption.Get("Player"); + this._appSetting = appSettingOption?.CurrentValue; + + Console.WriteLine($"AppSetting.Player.AppId = {this._appSetting.Player.AppId}"); + Console.WriteLine($"Player.AppId = {this._player.AppId}"); + } + + public string GetPlayerId() + { + return this._player.AppId; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs new file mode 100644 index 00000000..821b47da --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.Options; + +namespace NetFx48 +{ + public class AppServiceWithOptionsSnapshot : IAppService + { + private readonly AppSetting1 _appSetting; + private readonly Player1 _player; + + public AppServiceWithOptionsSnapshot(IOptionsSnapshot appSettingOption, + IOptionsSnapshot playerOption) + { + this._player = playerOption?.Value; + this._appSetting = appSettingOption?.Value; + + Console.WriteLine($"AppSetting.Player.AppId = {this._appSetting.Player.AppId}"); + Console.WriteLine($"Player.AppId = {this._player.AppId}"); + } + + public string GetPlayerId() + { + return this._player.AppId; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs index 95bb9f28..7b778d7d 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs @@ -6,4 +6,11 @@ public struct Player public string Key { get; set; } } + + public class Player1 + { + public string AppId { get; set; } + + public string Key { get; set; } + } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 8d0c27c2..32f1b6b1 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -1,9 +1,7 @@ using System; -using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace NetFx48 @@ -18,17 +16,21 @@ public class SurveyOptionTests .ConfigureAppConfiguration((hosting, configBuilder) => { // 1.ŪպA - var environmentName = hosting.Configuration["ENVIRONMENT2"]; - configBuilder.AddJsonFile("appsettings.json",false,true); - configBuilder.AddJsonFile($"appsettings.{environmentName}.json",true,true); + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); }) .ConfigureServices((hosting, services) => { // 2.`J Options services.AddOptions(); + // 3. `J IConfiguration services.Configure(hosting.Configuration); - + //`JLA services.AddSingleton(); }) @@ -36,7 +38,72 @@ public class SurveyOptionTests var host = builder.Build(); var service = host.Services.GetService(); var playerId = service.GetPlayerId(); - Console.WriteLine($"PlayerId={playerId}"); + Console.WriteLine($"PlayerId = {playerId}"); + } + + [TestMethod] + public void `JOptionMonitor() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // 1.ŪպA + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); + }) + .ConfigureServices((hosting, services) => + { + // `J Option M Configuration + services.Configure(hosting.Configuration); + + // `J Option MSw Configuration Section Name + services.Configure("Player", + hosting.Configuration.GetSection("Player")); + + //`JLA + services.AddScoped(); + }) + ; + var host = builder.Build(); + var service = host.Services.GetService(); + var playerId = service.GetPlayerId(); + Console.WriteLine($"PlayerId = {playerId}"); + } + + [TestMethod] + public void `JOptionSnapshot() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); + }) + .ConfigureServices((hosting, services) => + { + // `J Option by պA + services.Configure(hosting.Configuration); + + // `J Option by SwպA + services.Configure(hosting.Configuration + .GetSection("Player")); + + //`JLA + services.AddScoped(); + }) + ; + var host = builder.Build(); + var service = host.Services.GetService(); + var playerId = service.GetPlayerId(); + Console.WriteLine($"PlayerId = {playerId}"); } } } \ No newline at end of file From b1510ca564d8c23eb28a8398f3da7b9995f8e544 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 14:14:56 +0800 Subject: [PATCH 017/267] refactor --- .../AspNetCore3/StartupInjectionIOptionsMonitor.cs | 5 +---- .../Lab.Config/AspNetCore3/StartupInjectionOptions.cs | 5 +---- .../AspNetCore3/StartupInjectionOptionsSnapshot.cs | 11 +++-------- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs index cd938f30..9eaa24d2 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs @@ -38,10 +38,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J IOptions - services.AddOptions(); - - //`J IConfiguration + //`J Options M IConfiguration services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs index 70ec5f2d..e43e24eb 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs @@ -38,10 +38,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J IOptions - services.AddOptions(); - - //`J IConfiguration + //`J Options M IConfiguration services.Configure(this.Configuration); } } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs index 3b5933bd..c7ffc0ba 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs @@ -38,28 +38,23 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J IOptions - //services.AddOptions(); + // AppSetting services.AddOptions() .ValidateDataAnnotations() .Validate(p => { - if (p.AllowedHosts ==null) + if (p.AllowedHosts == null) { return false; } return true; }, "AllowedHosts must be value"); // Failure message. - ; - - //`J IConfiguration + //`J Options M IConfiguration services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); - - //services.AddSingleton(Configuration); } } } \ No newline at end of file From 3de04e8891d007b24eb87134dec304ad154f2af7 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 14:15:01 +0800 Subject: [PATCH 018/267] refactor --- .../NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 32f1b6b1..d4b51348 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -25,10 +25,7 @@ public class SurveyOptionTests }) .ConfigureServices((hosting, services) => { - // 2.`J Options - services.AddOptions(); - - // 3. `J IConfiguration + // 2. `J Option M Configuration services.Configure(hosting.Configuration); //`JLA From 1ee3b2a1ae1f7db568d6e98b265a4c9686d8bb69 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 15:01:06 +0800 Subject: [PATCH 019/267] refactor --- .../Controllers/DefaultController.cs | 16 ++++++++++---- .../NetCore/Lab.Config/AspNetCore3/Program.cs | 4 ++-- .../NetCore/Lab.Config/AspNetCore3/Startup.cs | 22 +++++++++++++++++++ .../AspNetCore3/StartupInjectionAppSetting.cs | 14 +++++++----- .../StartupInjectionIOptionsMonitor.cs | 5 ++++- .../AspNetCore3/StartupInjectionOptions.cs | 2 +- .../StartupInjectionOptionsSnapshot.cs | 6 ++++- .../Lab.Config/AspNetCore3/appsettings.json | 1 + 8 files changed, 55 insertions(+), 15 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs index 48fe7d64..c2653148 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -10,8 +10,7 @@ namespace AspNetCore3.Controllers [Route("[controller]")] public class DefaultController : ControllerBase { - // [Route("controller/appsettings")] - [Route("appsettings")] + [Route("options/appsettings")] public IActionResult Get() { // var setting = this.GetAppSetting(); @@ -19,8 +18,17 @@ public IActionResult Get() return this.Ok(setting); } - [Route("players/{id}")] - public IActionResult GetPlayer(int id) + [Route("monitor/players/{id}")] + public IActionResult GetMonitorPlayer(int id) + { + var serviceProvider = this.HttpContext.RequestServices; + var playerOption = serviceProvider.GetService>(); + var player = playerOption.Get($"Player{id}"); + return this.Ok(player); + } + + [Route("snapshot/players/{id}")] + public IActionResult GetSnapshotPlayer(int id) { var serviceProvider = this.HttpContext.RequestServices; var playerOption = serviceProvider.GetService>(); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs index a78569e1..be0f1227 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs @@ -20,10 +20,10 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { - // webBuilder.UseStartup(); + webBuilder.UseStartup(); // webBuilder.UseStartup(); // webBuilder.UseStartup(); - webBuilder.UseStartup(); + // webBuilder.UseStartup(); // webBuilder.UseStartup(); }); } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs index d4ba7974..edcdc609 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs @@ -37,6 +37,28 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void ConfigureServices(IServiceCollection services) { services.AddControllers(); + + // AppSetting + services.AddOptions() + .ValidateDataAnnotations() + .Validate(p => + { + if (p.AllowedHosts == null) + { + return false; + } + + return true; + }, "AllowedHosts must be value"); // Failure message. + + //`J Options M IConfiguration + services.Configure(this.Configuration); + + //`J Options M Configuration Section Name + services.Configure("Player1", this.Configuration.GetSection("Player1")); + services.Configure("Player2", this.Configuration.GetSection("Player2")); + services.Configure("Player3", this.Configuration.GetSection("Player3")); + services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings")); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs index 877559e9..9c6d3481 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs @@ -38,12 +38,14 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //var appSetting = new AppSetting(); - //this.Configuration.Bind(appSetting); - - ////`J AppSetting - //services.AddSingleton(appSetting); - services.Configure(this.Configuration); + //`J AppSetting + services.AddSingleton(provider => + { + //lazy load + var appSetting = new AppSetting(); + this.Configuration.Bind(appSetting); + return appSetting; + }); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs index 9eaa24d2..97d60096 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs @@ -38,7 +38,10 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J Options M IConfiguration + //`J Options M IConfiguration + services.Configure(this.Configuration); + + //`J Options M Configuration Section Name services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs index e43e24eb..178ddab3 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs @@ -38,7 +38,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J Options M IConfiguration + //`J Options M IConfiguration services.Configure(this.Configuration); } } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs index c7ffc0ba..44f45b23 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs @@ -51,10 +51,14 @@ public void ConfigureServices(IServiceCollection services) return true; }, "AllowedHosts must be value"); // Failure message. - //`J Options M IConfiguration + //`J Options M IConfiguration + services.Configure(this.Configuration); + + //`J Options M Configuration Section Name services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); + services.Configure("Player3", this.Configuration.GetSection("Player3")); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json index b00bf6b0..d157c06f 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -10,6 +10,7 @@ "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + "AuthenticationConnectionString": "" }, "Player1": { "AppId": "player1", From 59c970428a1c9c7160d3668a65882cc20f9b9d64 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 16:18:54 +0800 Subject: [PATCH 020/267] refactor --- .../Controllers/WeatherForecastController.cs | 192 +++++++++--------- .../NetCore/Lab.Config/AspNetCore3/Program.cs | 6 +- .../Properties/launchSettings.json | 4 +- .../NetCore/Lab.Config/AspNetCore3/Startup.cs | 8 +- .../Lab.Config/AspNetCore3/appsettings.json | 6 +- .../Lab.Config/AspNetCore5/AspNetCore5.csproj | 18 ++ .../Controllers/DefaultController.cs | 73 +++++++ .../Controllers/WeatherForecastController.cs | 39 ++++ .../NetCore/Lab.Config/AspNetCore5/Program.cs | 23 +++ .../Properties/launchSettings.json | 31 +++ .../NetCore/Lab.Config/AspNetCore5/Startup.cs | 71 +++++++ .../Lab.Config/AspNetCore5/WeatherForecast.cs | 15 ++ .../AspNetCore5/appsettings.Development.json | 9 + .../Lab.Config/AspNetCore5/appsettings.json | 28 +++ .../NetCore/Lab.Config/Lab.Config.sln | 8 + 15 files changed, 428 insertions(+), 103 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/Properties/launchSettings.json create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/WeatherForecast.cs create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.Development.json create mode 100644 Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs index f50947bf..5b4654df 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs @@ -1,95 +1,97 @@ -// using System; -// using System.Collections.Generic; -// using System.Linq; -// using Lab.Infra; -// using Microsoft.AspNetCore.Mvc; -// using Microsoft.Extensions.Configuration; -// using Microsoft.Extensions.Logging; -// using Microsoft.Extensions.Options; -// -// namespace AspNetCore3.Controllers -// { -// [ApiController] -// [Route("[controller]")] -// public class WeatherForecastController : ControllerBase -// { -// private static readonly string[] Summaries = -// { -// "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" -// }; -// -// private readonly ILogger _logger; -// private AppSetting _appSetting; -// private IConfiguration _config; -// private Player _player1; -// private Player _player2; -// -// // TODO:依賴 AppSetting -// // public WeatherForecastController(AppSetting appSetting) -// // { -// // this._appSetting = appSetting; -// // } -// -// // TODO:依賴 IOptions -// public WeatherForecastController(IOptions options) -// { -// try -// { -// this._appSetting = options.Value; -// } -// catch (OptionsValidationException ex) -// { -// foreach (var failure in ex.Failures) -// { -// Console.WriteLine(failure); -// } -// } -// } -// -// // TODO:依賴 IOptionsSnapshot -// //public WeatherForecastController(IOptionsSnapshot options) -// //{ -// // this._player1 = options.Get("Player1"); -// // this._player2 = options.Get("Player2"); -// //} -// -// //// TODO:依賴 IOptionsMonitor -// //public WeatherForecastController(IOptionsMonitor options) -// //{ -// // this._player1 = options.Get("Player1"); -// // this._player2 = options.Get("Player2"); -// //} -// -// // TODO:依賴 IConfiguration -// //public WeatherForecastController(IConfiguration config) -// //{ -// // this._config = config; -// //} -// -// // public WeatherForecastController(IOptions options, IConfiguration config) -// // { -// // this._config = config; -// // var appSetting = new AppSetting(); -// // config.Bind(appSetting); -// // this._appSetting = options.Value; -// // } -// -// //public WeatherForecastController(ILogger logger) -// //{ -// // _logger = logger; -// //} -// -// [HttpGet] -// public IEnumerable Get() -// { -// var rng = new Random(); -// return Enumerable.Range(1, 5).Select(index => new WeatherForecast -// { -// Date = DateTime.Now.AddDays(index), -// TemperatureC = rng.Next(-20, 55), -// Summary = Summaries[rng.Next(Summaries.Length)] -// }) -// .ToArray(); -// } -// } -// } \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Linq; +using Lab.Infra; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace AspNetCore3.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + private AppSetting _appSetting; + private IConfiguration _config; + private Player _player1; + private Player _player2; + + // TODO:依賴 AppSetting + // public WeatherForecastController(AppSetting appSetting) + // { + // this._appSetting = appSetting; + // } + + // TODO:依賴 IOptions + public WeatherForecastController(IOptions options) + { + + try + { + this._appSetting = options.Value; + } + catch (OptionsValidationException ex) + { + foreach (var failure in ex.Failures) + { + Console.WriteLine(failure); + } + } + } + + // TODO:依賴 IOptionsSnapshot + //public WeatherForecastController(IOptionsSnapshot options) + //{ + // this._player1 = options.Get("Player1"); + // this._player2 = options.Get("Player2"); + //} + + //// TODO:依賴 IOptionsMonitor + //public WeatherForecastController(IOptionsMonitor options) + //{ + // this._player1 = options.Get("Player1"); + // this._player2 = options.Get("Player2"); + //} + + // TODO:依賴 IConfiguration + //public WeatherForecastController(IConfiguration config) + //{ + // this._config = config; + //} + + // public WeatherForecastController(IOptions options, IConfiguration config) + // { + // this._config = config; + // var appSetting = new AppSetting(); + // config.Bind(appSetting); + // this._appSetting = options.Value; + // } + + //public WeatherForecastController(ILogger logger) + //{ + // _logger = logger; + //} + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs index be0f1227..83e04777 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs @@ -19,7 +19,11 @@ public static void Main(string[] args) public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => - { + { + webBuilder.ConfigureAppConfiguration(p => + { + p.AddJsonFile("appsettings.json", false, false); + }); webBuilder.UseStartup(); // webBuilder.UseStartup(); // webBuilder.UseStartup(); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json index 649d735f..546f5d39 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json @@ -12,8 +12,8 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "//launchUrl": "weatherforecast", - "launchUrl": "default", + "launchUrl": "weatherforecast", + "//launchUrl": "default", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs index edcdc609..5faec907 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs @@ -55,10 +55,10 @@ public void ConfigureServices(IServiceCollection services) services.Configure(this.Configuration); //`J Options M Configuration Section Name - services.Configure("Player1", this.Configuration.GetSection("Player1")); - services.Configure("Player2", this.Configuration.GetSection("Player2")); - services.Configure("Player3", this.Configuration.GetSection("Player3")); - services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings")); + // services.Configure("Player1", this.Configuration.GetSection("Player1")); + // services.Configure("Player2", this.Configuration.GetSection("Player2")); + // services.Configure("Player3", this.Configuration.GetSection("Player3")); + // services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings")); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json index d157c06f..829fc2f8 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -9,9 +9,13 @@ "AllowedHosts": "*", "ConnectionStrings": { - "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;", "AuthenticationConnectionString": "" }, + "Player": { + "AppId": "player", + "Key": "1234567890" + }, "Player1": { "AppId": "player1", "Key": "12345678990" diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj new file mode 100644 index 00000000..eae77f5a --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + bin + bin\AspNetCore5.xml + + + + + + + + + + + + diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs new file mode 100644 index 00000000..ee675c6b --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs @@ -0,0 +1,73 @@ +using System; +using Lab.Infra; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace AspNetCore5.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + [HttpGet] + [Route("options/appsettings")] + public IActionResult Get() + { + var serviceProvider = this.HttpContext.RequestServices; + var content = serviceProvider.GetService>()?.Value; + return this.Ok(content); + } + + [HttpGet] + [Route("monitor/players/{id}")] + public IActionResult GetMonitorPlayer(int id) + { + var serviceProvider = this.HttpContext.RequestServices; + var appSettingOptions = serviceProvider.GetService>(); + var playerOptions = serviceProvider.GetService>(); + var content = new + { + App = appSettingOptions?.CurrentValue, + Player = playerOptions?.Get($"Player{id}") + }; + return this.Ok(content); + } + + [HttpGet] + [Route("snapshot/players/{id}")] + public IActionResult GetSnapshotPlayer(int id) + { + var serviceProvider = this.HttpContext.RequestServices; + var appSettingOptions = serviceProvider.GetService>(); + var playerOptions = serviceProvider.GetService>(); + var content = new + { + App = appSettingOptions?.Value, + Player = playerOptions?.Get($"Player{id}") + }; + return this.Ok(content); + } + + private AppSetting GetAppSetting() + { + var serviceProvider = this.HttpContext.RequestServices; + var options = serviceProvider.GetService>(); + return options?.Value; + } + + private AppSetting GetAppSettingMonitor() + { + var serviceProvider = this.HttpContext.RequestServices; + var appsSettingOptions = serviceProvider.GetService>(); + var playerOption = serviceProvider.GetService>(); + var player1 = playerOption.Get("Player1"); + var player2 = playerOption.Get("Player2"); + Console.WriteLine($"player1={player1.AppId}"); + Console.WriteLine($"player2={player2.AppId}"); + + // var appSetting = options.Get("Player1"); + return appsSettingOptions?.CurrentValue; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..932d39a7 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs @@ -0,0 +1,39 @@ +// using System; +// using System.Collections.Generic; +// using System.Linq; +// using System.Threading.Tasks; +// using Microsoft.AspNetCore.Mvc; +// using Microsoft.Extensions.Logging; +// +// namespace AspNetCore5.Controllers +// { +// [ApiController] +// [Route("[controller]")] +// public class WeatherForecastController : ControllerBase +// { +// private static readonly string[] Summaries = new[] +// { +// "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +// }; +// +// private readonly ILogger _logger; +// +// public WeatherForecastController(ILogger logger) +// { +// _logger = logger; +// } +// +// [HttpGet] +// public IEnumerable Get() +// { +// var rng = new Random(); +// return Enumerable.Range(1, 5).Select(index => new WeatherForecast +// { +// Date = DateTime.Now.AddDays(index), +// TemperatureC = rng.Next(-20, 55), +// Summary = Summaries[rng.Next(Summaries.Length)] +// }) +// .ToArray(); +// } +// } +// } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs new file mode 100644 index 00000000..2c140db5 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace AspNetCore5 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/AspNetCore5/Properties/launchSettings.json new file mode 100644 index 00000000..b38cbf0c --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32162", + "sslPort": 44347 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCore5": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs new file mode 100644 index 00000000..6fbe80e2 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs @@ -0,0 +1,71 @@ +using Lab.Infra; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +namespace AspNetCore5 +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AspNetCore5 v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo {Title = "AspNetCore5", Version = "v1"}); + }); + + // AppSetting + services.AddOptions() + .ValidateDataAnnotations() + .Validate(p => + { + if (p.AllowedHosts == null) + { + return false; + } + + return true; + }, "AllowedHosts must be value"); // Failure message. + + //`J Options M IConfiguration + services.Configure(this.Configuration); + + //`J Options M Configuration Section Name + services.Configure("Player1", this.Configuration.GetSection("Player1")); + services.Configure("Player2", this.Configuration.GetSection("Player2")); + services.Configure("Player3", this.Configuration.GetSection("Player3")); + services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings")); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/WeatherForecast.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/WeatherForecast.cs new file mode 100644 index 00000000..393f1d76 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace AspNetCore5 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.Development.json b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json new file mode 100644 index 00000000..559f064e --- /dev/null +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json @@ -0,0 +1,28 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "ConnectionStrings": { + "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;", + "AuthenticationConnectionString": "" + }, + "Player1": { + "AppId": "player1", + "Key": "12345678990" + }, + + "Player2": { + "AppId": "player2", + "Key": "player2_123456" + }, + "Player3": { + "AppId": "player3", + "Key": "player3_123456" + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/Lab.Config.sln b/Configuration/NetCore/Lab.Config/Lab.Config.sln index b490ce4e..11296a46 100644 --- a/Configuration/NetCore/Lab.Config/Lab.Config.sln +++ b/Configuration/NetCore/Lab.Config/Lab.Config.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore3", "AspNetCore3\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFx48", "NetFx48\NetFx48.csproj", "{D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore5", "AspNetCore5\AspNetCore5.csproj", "{5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +44,12 @@ Global {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.Release|Any CPU.Build.0 = Release|Any CPU {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.QA|Any CPU.ActiveCfg = QA|Any CPU {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.QA|Any CPU.Build.0 = QA|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.Release|Any CPU.Build.0 = Release|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.QA|Any CPU.ActiveCfg = Debug|Any CPU + {5D04A967-5E69-4CD7-AE41-F8BAD30D7DC2}.QA|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From b271db9d07ad6c49bbfafabafc98d1674577e5e5 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 1 Apr 2021 17:20:21 +0800 Subject: [PATCH 021/267] refactor --- .../Lab.Config/AspNetCore5/AspNetCore5.csproj | 1 + .../Controllers/DefaultController.cs | 12 ++++++++++ .../NetCore/Lab.Config/AspNetCore5/Program.cs | 24 +++++++++++-------- .../NetCore/Lab.Config/AspNetCore5/Startup.cs | 8 +++++++ .../Lab.Config/AspNetCore5/appsettings.json | 4 ++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj index eae77f5a..60048b36 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj @@ -8,6 +8,7 @@ + diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs index ee675c6b..db606bcc 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs @@ -1,6 +1,7 @@ using System; using Lab.Infra; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -19,6 +20,17 @@ public IActionResult Get() return this.Ok(content); } + [HttpGet] + [Route("config/appsettings")] + public IActionResult GetConfig() + { + var serviceProvider = this.HttpContext.RequestServices; + var config = serviceProvider.GetService(); + var content = new AppSetting(); + config.Bind(content); + return this.Ok(content); + } + [HttpGet] [Route("monitor/players/{id}")] public IActionResult GetMonitorPlayer(int id) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs index 2c140db5..861aa8ce 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs @@ -1,23 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; namespace AspNetCore5 { public class Program { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureAppConfiguration(p => + { + // sJպA + //p.AddJsonFile("appsettings.json", false, false); + }); + webBuilder.UseStartup(); + }); + } + public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs index 6fbe80e2..1a7bd75c 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs @@ -66,6 +66,14 @@ public void ConfigureServices(IServiceCollection services) services.Configure("Player2", this.Configuration.GetSection("Player2")); services.Configure("Player3", this.Configuration.GetSection("Player3")); services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings")); + // services.PostConfigure("Player1", config => + // { + // config.AppId = "post_configured_option1_value"; + // }); + // services.PostConfigureAll(config => + // { + // config.Player.AppId = "post_configured_option1_value"; + // }); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json index 559f064e..ee3bc5a4 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json @@ -12,6 +12,10 @@ "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;", "AuthenticationConnectionString": "" }, + "Player": { + "AppId": "player23", + "Key": "1234567890" + }, "Player1": { "AppId": "player1", "Key": "12345678990" From 300ce4a91c1170365b7cebde302ad2d9dd92928d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 12:41:51 +0800 Subject: [PATCH 022/267] refactor --- .../NetCore/Lab.Config/NetFx48/AppService.cs | 40 ------------------- .../NetCore/Lab.Config/NetFx48/AppWorkFlow.cs | 19 +++++++++ .../NetFx48/AppWorkFlowWithOption.cs | 19 +++++++++ ...or.cs => AppWorkFlowWithOptionsMonitor.cs} | 4 +- ...t.cs => AppWorkFlowWithOptionsSnapshot.cs} | 4 +- .../Lab.Config/NetFx48/IAppWorkFlow.cs | 7 ++++ .../SurveyCommandConfigurationTests.cs | 2 +- .../NetFx48/SurveyJsonConfigurationTests.cs | 4 +- .../Lab.Config/NetFx48/SurveyOptionTests.cs | 12 +++--- 9 files changed, 58 insertions(+), 53 deletions(-) delete mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppService.cs create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow.cs create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs rename Configuration/NetCore/Lab.Config/NetFx48/{AppServiceWithOptionsMonitor.cs => AppWorkFlowWithOptionsMonitor.cs} (81%) rename Configuration/NetCore/Lab.Config/NetFx48/{AppServiceWithOptionsSnapshot.cs => AppWorkFlowWithOptionsSnapshot.cs} (80%) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/IAppWorkFlow.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs deleted file mode 100644 index 7bf16e6f..00000000 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppService.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; - -namespace NetFx48 -{ - public interface IAppService - { - string GetPlayerId(); - } - - public class AppService : IAppService - { - private readonly IConfiguration _config; - - public AppService(IConfiguration config) - { - this._config = config; - } - - public string GetPlayerId() - { - return this._config.GetSection("Player:AppId").Value; - } - } - - public class AppServiceWithOption : IAppService - { - private readonly AppSetting1 _appSetting; - - public AppServiceWithOption(IOptions options) - { - this._appSetting = options.Value; - } - - public string GetPlayerId() - { - return this._appSetting.Player.AppId; - } - } -} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow.cs new file mode 100644 index 00000000..85244965 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; + +namespace NetFx48 +{ + public class AppWorkFlow : IAppWorkFlow + { + private readonly IConfiguration _config; + + public AppWorkFlow(IConfiguration config) + { + this._config = config; + } + + public string GetPlayerId() + { + return this._config.GetSection("Player:AppId").Value; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs new file mode 100644 index 00000000..d6e6328b --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; + +namespace NetFx48 +{ + public class AppWorkFlowWithOption : IAppWorkFlow + { + private readonly AppSetting1 _appSetting; + + public AppWorkFlowWithOption(IOptions options) + { + this._appSetting = options.Value; + } + + public string GetPlayerId() + { + return this._appSetting.Player.AppId; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs similarity index 81% rename from Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs rename to Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs index 90a837ff..97fe82cb 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsMonitor.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs @@ -3,12 +3,12 @@ namespace NetFx48 { - public class AppServiceWithOptionsMonitor : IAppService + public class AppWorkFlowWithOptionsMonitor : IAppWorkFlow { private readonly AppSetting1 _appSetting; private readonly Player1 _player; - public AppServiceWithOptionsMonitor(IOptionsMonitor appSettingOption, + public AppWorkFlowWithOptionsMonitor(IOptionsMonitor appSettingOption, IOptionsMonitor playerOption) { this._player = playerOption.Get("Player"); diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsSnapshot.cs similarity index 80% rename from Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs rename to Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsSnapshot.cs index 821b47da..72a1f7c6 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppServiceWithOptionsSnapshot.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsSnapshot.cs @@ -3,12 +3,12 @@ namespace NetFx48 { - public class AppServiceWithOptionsSnapshot : IAppService + public class AppWorkFlowWithOptionsSnapshot : IAppWorkFlow { private readonly AppSetting1 _appSetting; private readonly Player1 _player; - public AppServiceWithOptionsSnapshot(IOptionsSnapshot appSettingOption, + public AppWorkFlowWithOptionsSnapshot(IOptionsSnapshot appSettingOption, IOptionsSnapshot playerOption) { this._player = playerOption?.Value; diff --git a/Configuration/NetCore/Lab.Config/NetFx48/IAppWorkFlow.cs b/Configuration/NetCore/Lab.Config/NetFx48/IAppWorkFlow.cs new file mode 100644 index 00000000..3c7de088 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/IAppWorkFlow.cs @@ -0,0 +1,7 @@ +namespace NetFx48 +{ + public interface IAppWorkFlow + { + string GetPlayerId(); + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs index 7c00b012..6e1eaddb 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs @@ -58,7 +58,7 @@ public void 命令對應_Host(string[] args) .ConfigureServices(service => { //DI - service.AddScoped(typeof(AppService)); + service.AddScoped(typeof(AppWorkFlow)); }) ; var host = builder.Build(); diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 537cba32..05f034b6 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -67,11 +67,11 @@ public void 注入Configuration() .ConfigureServices(service => { //DI - service.AddScoped(typeof(AppService)); + service.AddScoped(typeof(AppWorkFlow)); }); var host = builder.Build(); - var appService = host.Services.GetService(); + var appService = host.Services.GetService(); var playerId = appService.GetPlayerId(); Console.WriteLine($"AppId = {playerId}"); } diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index d4b51348..8a9edb35 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -29,11 +29,11 @@ public class SurveyOptionTests services.Configure(hosting.Configuration); //`JLA - services.AddSingleton(); + services.AddSingleton(); }) ; var host = builder.Build(); - var service = host.Services.GetService(); + var service = host.Services.GetService(); var playerId = service.GetPlayerId(); Console.WriteLine($"PlayerId = {playerId}"); } @@ -62,11 +62,11 @@ public class SurveyOptionTests hosting.Configuration.GetSection("Player")); //`JLA - services.AddScoped(); + services.AddScoped(); }) ; var host = builder.Build(); - var service = host.Services.GetService(); + var service = host.Services.GetService(); var playerId = service.GetPlayerId(); Console.WriteLine($"PlayerId = {playerId}"); } @@ -94,11 +94,11 @@ public class SurveyOptionTests .GetSection("Player")); //`JLA - services.AddScoped(); + services.AddScoped(); }) ; var host = builder.Build(); - var service = host.Services.GetService(); + var service = host.Services.GetService(); var playerId = service.GetPlayerId(); Console.WriteLine($"PlayerId = {playerId}"); } From 9e574c0cde4afef3cfebe1ad9867e698847d7f01 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 13:06:16 +0800 Subject: [PATCH 023/267] refactor --- .../Lab.Config/AspNetCore5/Controllers/DefaultController.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs index db606bcc..1c9c318c 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs @@ -43,6 +43,10 @@ public IActionResult GetMonitorPlayer(int id) App = appSettingOptions?.CurrentValue, Player = playerOptions?.Get($"Player{id}") }; + appSettingOptions.OnChange(p => + { + Console.WriteLine("`Iwܧ"); + }); return this.Ok(content); } From d71018660f52cb1809bddbd673aced078fd91a3d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 13:41:27 +0800 Subject: [PATCH 024/267] refactor --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 1 + .../Lab.Config/NetFx48/SurveyOptionTests.cs | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index 679a98ac..954e0d74 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -28,6 +28,7 @@ + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 8a9edb35..7106c0b3 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -102,5 +102,49 @@ public class SurveyOptionTests var playerId = service.GetPlayerId(); Console.WriteLine($"PlayerId = {playerId}"); } + + [TestMethod] + public void () + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // 1.ŪպA + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); + }) + .ConfigureServices((hosting, services) => + { + // 2. `J Option M Configuration + services.Configure(hosting.Configuration); + // + services.AddOptions() + .ValidateDataAnnotations() + .Validate(p => + { + if (p.ConnectionStrings + .DefaultConnectionString == null) + { + return false; + } + + return true; + }, + "DefaultConnectionString must be value"); // Failure message. + ; + + //`JLA + services.AddSingleton(); + }) + ; + var host = builder.Build(); + var service = host.Services.GetService(); + var playerId = service.GetPlayerId(); + Console.WriteLine($"PlayerId = {playerId}"); + } } } \ No newline at end of file From 5f5bfa3776d2c63fd0a3af4798bfdb2fe7258fc9 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 13:59:30 +0800 Subject: [PATCH 025/267] refactor --- .../Lab.Config/NetFx48/AppWorkFlowWithOption.cs | 15 +++++++++++++-- .../Lab.Config/NetFx48/SurveyOptionTests.cs | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs index d6e6328b..654999c7 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Options; +using System; +using Microsoft.Extensions.Options; namespace NetFx48 { @@ -8,7 +9,17 @@ public class AppWorkFlowWithOption : IAppWorkFlow public AppWorkFlowWithOption(IOptions options) { - this._appSetting = options.Value; + try + { + this._appSetting = options.Value; + } + catch (OptionsValidationException ex) + { + foreach (var failure in ex.Failures) + { + Console.WriteLine(failure); + } + } } public string GetPlayerId() diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 7106c0b3..985fc309 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -126,8 +126,8 @@ public class SurveyOptionTests .ValidateDataAnnotations() .Validate(p => { - if (p.ConnectionStrings - .DefaultConnectionString == null) + var hasContent = string.IsNullOrWhiteSpace(p.ConnectionStrings.DefaultConnectionString); + if (hasContent == false) { return false; } From 8bfee5b5db2d34885324084e005c26a8777562fc Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 14:17:59 +0800 Subject: [PATCH 026/267] refactor --- .../Controllers/DefaultController.cs | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs index c2653148..0ad61e02 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -1,4 +1,3 @@ -using System; using Lab.Infra; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; @@ -13,9 +12,9 @@ public class DefaultController : ControllerBase [Route("options/appsettings")] public IActionResult Get() { - // var setting = this.GetAppSetting(); - var setting = this.GetAppSettingMonitor(); - return this.Ok(setting); + var serviceProvider = this.HttpContext.RequestServices; + var options = serviceProvider.GetService>(); + return this.Ok(options?.Value); } [Route("monitor/players/{id}")] @@ -35,26 +34,5 @@ public IActionResult GetSnapshotPlayer(int id) var player = playerOption.Get($"Player{id}"); return this.Ok(player); } - - private AppSetting GetAppSetting() - { - var serviceProvider = this.HttpContext.RequestServices; - var options = serviceProvider.GetService>(); - return options?.Value; - } - - private AppSetting GetAppSettingMonitor() - { - var serviceProvider = this.HttpContext.RequestServices; - var appsSettingOptions = serviceProvider.GetService>(); - var playerOption = serviceProvider.GetService>(); - var player1 = playerOption.Get("Player1"); - var player2 = playerOption.Get("Player2"); - Console.WriteLine($"player1={player1.AppId}"); - Console.WriteLine($"player2={player2.AppId}"); - - // var appSetting = options.Get("Player1"); - return appsSettingOptions?.CurrentValue; - } } } \ No newline at end of file From d6439753721f08b63e11bc4777f6aaaccdd54e42 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 2 Apr 2021 14:39:42 +0800 Subject: [PATCH 027/267] refactor --- .../Lab.Config/NetFx48/AppWorkFlow1.cs | 16 ++++++++++ .../NetFx48/AppWorkFlowWithOption.cs | 2 +- .../Lab.Config/NetFx48/SurveyOptionTests.cs | 32 ++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow1.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow1.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow1.cs new file mode 100644 index 00000000..dfaf1482 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlow1.cs @@ -0,0 +1,16 @@ +namespace NetFx48 +{ + public class AppWorkFlow1 : IAppWorkFlow + { + private AppSetting _appSetting; + + public AppWorkFlow1(AppSetting appSetting) + { + this._appSetting = appSetting; + } + public string GetPlayerId() + { + return this._appSetting.Player.AppId; + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs index 654999c7..6dd2db39 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs @@ -21,7 +21,7 @@ public AppWorkFlowWithOption(IOptions options) } } } - + public string GetPlayerId() { return this._appSetting.Player.AppId; diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 985fc309..6845a811 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -103,7 +103,7 @@ public class SurveyOptionTests Console.WriteLine($"PlayerId = {playerId}"); } - [TestMethod] +[TestMethod] public void () { var builder = Host.CreateDefaultBuilder() @@ -146,5 +146,35 @@ public class SurveyOptionTests var playerId = service.GetPlayerId(); Console.WriteLine($"PlayerId = {playerId}"); } + + [TestMethod] + public void `JպA() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hosting, configBuilder) => + { + // 1.ŪպA + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); + }) + .ConfigureServices((hosting, services) => + { + var appSetting = hosting.Configuration.Get(); + services.AddSingleton(typeof(AppSetting), appSetting); + + //`JLA + services.AddSingleton(); + }) + ; + var host = builder.Build(); + var service = host.Services.GetService(); + var playerId = service.GetPlayerId(); + Console.WriteLine($"PlayerId = {playerId}"); + } + } } \ No newline at end of file From 2039ba9681ea8a21296a95cfc806f8726f452aaf Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sun, 4 Apr 2021 12:25:47 +0800 Subject: [PATCH 028/267] refactor --- .../Controllers/DefaultController.cs | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs index 1c9c318c..3f0d4c4b 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs @@ -64,26 +64,5 @@ public IActionResult GetSnapshotPlayer(int id) }; return this.Ok(content); } - - private AppSetting GetAppSetting() - { - var serviceProvider = this.HttpContext.RequestServices; - var options = serviceProvider.GetService>(); - return options?.Value; - } - - private AppSetting GetAppSettingMonitor() - { - var serviceProvider = this.HttpContext.RequestServices; - var appsSettingOptions = serviceProvider.GetService>(); - var playerOption = serviceProvider.GetService>(); - var player1 = playerOption.Get("Player1"); - var player2 = playerOption.Get("Player2"); - Console.WriteLine($"player1={player1.AppId}"); - Console.WriteLine($"player2={player2.AppId}"); - - // var appSetting = options.Get("Player1"); - return appsSettingOptions?.CurrentValue; - } } } \ No newline at end of file From 5ce55a5ca040a9343bf8841c25780045d2fa373e Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sun, 4 Apr 2021 16:45:55 +0800 Subject: [PATCH 029/267] refactor --- Host/ConsoleAppNetFx48/AppHost.cs | 35 +++++++++++++ .../ConsoleAppNetFx48.csproj | 11 ++++ .../ConsoleAppNetFx48/LabBackgroundService.cs | 27 ++++++++++ Host/ConsoleAppNetFx48/LabHostedService.cs | 51 +++++++++++++++++++ Host/ConsoleAppNetFx48/Program.cs | 12 +++++ Host/Lab.MsHost.sln | 16 ++++-- Host/NetFx48/AppHost.cs | 35 +++++++++++++ Host/NetFx48/LabBackgroundService.cs | 27 ++++++++++ Host/NetFx48/LabHostedService.cs | 51 +++++++++++++++++++ Host/NetFx48/NetFx48.csproj | 11 ++++ Host/NetFx48/Program.cs | 12 +++++ 11 files changed, 283 insertions(+), 5 deletions(-) create mode 100644 Host/ConsoleAppNetFx48/AppHost.cs create mode 100644 Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj create mode 100644 Host/ConsoleAppNetFx48/LabBackgroundService.cs create mode 100644 Host/ConsoleAppNetFx48/LabHostedService.cs create mode 100644 Host/ConsoleAppNetFx48/Program.cs create mode 100644 Host/NetFx48/AppHost.cs create mode 100644 Host/NetFx48/LabBackgroundService.cs create mode 100644 Host/NetFx48/LabHostedService.cs create mode 100644 Host/NetFx48/NetFx48.csproj create mode 100644 Host/NetFx48/Program.cs diff --git a/Host/ConsoleAppNetFx48/AppHost.cs b/Host/ConsoleAppNetFx48/AppHost.cs new file mode 100644 index 00000000..23d90627 --- /dev/null +++ b/Host/ConsoleAppNetFx48/AppHost.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace ConsoleAppNetFx48 +{ + public class AppHost : IHostedService + { + private readonly ILogger logger; + private readonly IHostApplicationLifetime appLifetime; + + public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) + { + this.logger = logger; + this.appLifetime = appLifetime; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); + + await Task.Yield(); + + this.appLifetime.StopApplication(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj new file mode 100644 index 00000000..bd4231b7 --- /dev/null +++ b/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -0,0 +1,11 @@ + + + + Exe + net5.0 + + + + + + diff --git a/Host/ConsoleAppNetFx48/LabBackgroundService.cs b/Host/ConsoleAppNetFx48/LabBackgroundService.cs new file mode 100644 index 00000000..6436c681 --- /dev/null +++ b/Host/ConsoleAppNetFx48/LabBackgroundService.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace ConsoleAppNetFx48 +{ + public class LabBackgroundService : BackgroundService + { + private readonly ILogger _logger; + + public LabBackgroundService(ILogger logger) + { + this._logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await Task.Delay(1000, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/LabHostedService.cs b/Host/ConsoleAppNetFx48/LabHostedService.cs new file mode 100644 index 00000000..f97f2467 --- /dev/null +++ b/Host/ConsoleAppNetFx48/LabHostedService.cs @@ -0,0 +1,51 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace ConsoleAppNetFx48 +{ + public class LabHostedService : IHostedService + { + private readonly ILogger _logger; + + public LabHostedService(ILogger logger, + IHostApplicationLifetime lifetime) + { + this._logger = logger; + + lifetime.ApplicationStarted.Register(this.OnStarted); + lifetime.ApplicationStopping.Register(this.OnStopping); + lifetime.ApplicationStopped.Register(this.OnStopped); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("1. StartAsync has been called."); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("4. StopAsync has been called."); + + return Task.CompletedTask; + } + + private void OnStarted() + { + this._logger.LogInformation("2. OnStarted has been called."); + } + + private void OnStopped() + { + this._logger.LogInformation("5. OnStopped has been called."); + } + + private void OnStopping() + { + this._logger.LogInformation("3. OnStopping has been called."); + } + } +} \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/Program.cs b/Host/ConsoleAppNetFx48/Program.cs new file mode 100644 index 00000000..1c21da12 --- /dev/null +++ b/Host/ConsoleAppNetFx48/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace ConsoleAppNetFx48 +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} \ No newline at end of file diff --git a/Host/Lab.MsHost.sln b/Host/Lab.MsHost.sln index ce7c2c7d..ea780bc4 100644 --- a/Host/Lab.MsHost.sln +++ b/Host/Lab.MsHost.sln @@ -2,7 +2,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNet5", "ConsoleAppNet5\ConsoleAppNet5.csproj", "{1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNet48", "ConsoleAppNet48\ConsoleAppNet48.csproj", "{069BA841-E538-4E51-8D1D-F175107B1312}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFx48", "NetFx48\NetFx48.csproj", "{B565ABD2-BC7D-44B4-8202-EDD30B2FB260}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNetFx48", "ConsoleAppNetFx48\ConsoleAppNetFx48.csproj", "{45E650BE-BBBF-4060-B2CB-049C45B6830D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -14,9 +16,13 @@ Global {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Release|Any CPU.Build.0 = Release|Any CPU - {069BA841-E538-4E51-8D1D-F175107B1312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {069BA841-E538-4E51-8D1D-F175107B1312}.Debug|Any CPU.Build.0 = Debug|Any CPU - {069BA841-E538-4E51-8D1D-F175107B1312}.Release|Any CPU.ActiveCfg = Release|Any CPU - {069BA841-E538-4E51-8D1D-F175107B1312}.Release|Any CPU.Build.0 = Release|Any CPU + {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Release|Any CPU.Build.0 = Release|Any CPU + {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Host/NetFx48/AppHost.cs b/Host/NetFx48/AppHost.cs new file mode 100644 index 00000000..04545531 --- /dev/null +++ b/Host/NetFx48/AppHost.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace NetFx48 +{ + public class AppHost : IHostedService + { + private readonly ILogger logger; + private readonly IHostApplicationLifetime appLifetime; + + public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) + { + this.logger = logger; + this.appLifetime = appLifetime; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); + + await Task.Yield(); + + this.appLifetime.StopApplication(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Host/NetFx48/LabBackgroundService.cs b/Host/NetFx48/LabBackgroundService.cs new file mode 100644 index 00000000..69048bdd --- /dev/null +++ b/Host/NetFx48/LabBackgroundService.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace NetFx48 +{ + public class LabBackgroundService : BackgroundService + { + private readonly ILogger _logger; + + public LabBackgroundService(ILogger logger) + { + this._logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await Task.Delay(1000, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/Host/NetFx48/LabHostedService.cs b/Host/NetFx48/LabHostedService.cs new file mode 100644 index 00000000..a573516d --- /dev/null +++ b/Host/NetFx48/LabHostedService.cs @@ -0,0 +1,51 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace NetFx48 +{ + public class LabHostedService : IHostedService + { + private readonly ILogger _logger; + + public LabHostedService(ILogger logger, + IHostApplicationLifetime lifetime) + { + this._logger = logger; + + lifetime.ApplicationStarted.Register(this.OnStarted); + lifetime.ApplicationStopping.Register(this.OnStopping); + lifetime.ApplicationStopped.Register(this.OnStopped); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("1. StartAsync has been called."); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("4. StopAsync has been called."); + + return Task.CompletedTask; + } + + private void OnStarted() + { + this._logger.LogInformation("2. OnStarted has been called."); + } + + private void OnStopped() + { + this._logger.LogInformation("5. OnStopped has been called."); + } + + private void OnStopping() + { + this._logger.LogInformation("3. OnStopping has been called."); + } + } +} \ No newline at end of file diff --git a/Host/NetFx48/NetFx48.csproj b/Host/NetFx48/NetFx48.csproj new file mode 100644 index 00000000..bd4231b7 --- /dev/null +++ b/Host/NetFx48/NetFx48.csproj @@ -0,0 +1,11 @@ + + + + Exe + net5.0 + + + + + + diff --git a/Host/NetFx48/Program.cs b/Host/NetFx48/Program.cs new file mode 100644 index 00000000..b484980b --- /dev/null +++ b/Host/NetFx48/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace NetFx48 +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} \ No newline at end of file From 644970e7eb98d116ee4ddc7e5ff10f92e50078a8 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 6 Apr 2021 13:28:13 +0800 Subject: [PATCH 030/267] refactor --- ...yEnvironmentVariablesConfigurationTests.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs index 53043e3f..f5bdd59a 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -16,7 +17,8 @@ public void Host實例化ConfigurationBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => { // config.Sources.Clear(); - + var hostingEnvironmentEnvironmentName = + hosting.HostingEnvironment.EnvironmentName; configBuilder.AddEnvironmentVariables("Custom_"); var configRoot = configBuilder.Build(); @@ -24,14 +26,16 @@ public void Host實例化ConfigurationBuilder() Console .WriteLine($"ASPNETCORE_ENVIRONMENT = {configRoot["ASPNETCORE_ENVIRONMENT"]}"); Console - .WriteLine($"DOTNET_ENVIRONMENT2 = {configRoot["DOTNET_ENVIRONMENT2"]}"); + .WriteLine($"DOTNET_ENVIRONMENT = {configRoot["DOTNET_ENVIRONMENT"]}"); Console - .WriteLine($"CUSTOM_ENVIRONMENT1 = {configRoot["CUSTOM_ENVIRONMENT1"]}"); + .WriteLine($"CUSTOM_ENVIRONMENT = {configRoot["CUSTOM_ENVIRONMENT"]}"); Console .WriteLine($"ENVIRONMENT1 = {configRoot["ENVIRONMENT1"]}"); }) ; - builder.Build(); + var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + Console.WriteLine($"EnvironmentName={environment.EnvironmentName}"); } [TestMethod] @@ -41,16 +45,20 @@ public void 切換組態設定() .ConfigureAppConfiguration((hosting, configBuilder) => { // config.Sources.Clear(); - var environmentName = hosting.Configuration["ENVIRONMENT2"]; - configBuilder.AddJsonFile("appsettings.json",false,true); - configBuilder.AddJsonFile($"appsettings.{environmentName}.json",true,true); + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); var configRoot = configBuilder.Build(); - + //讀取組態 Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + Console + .WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); }) ; builder.Build(); From 0100dc409c9cb51ab371347e109bc9a09fce518f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 6 Apr 2021 15:38:51 +0800 Subject: [PATCH 031/267] refactor --- ...urveyEnvironmentVariablesConfigurationTests.cs | 15 +++++++++++++++ .../NetCore/Lab.Config/NetFx48/appsettings.json | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs index f5bdd59a..bf710645 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs @@ -77,5 +77,20 @@ public void 手動實例化ConfigurationBuilder() //讀取組態 Console.WriteLine($"ENVIRONMENT = {configRoot["ENVIRONMENT"]}"); } + + [TestMethod] + public void 設定主機組態() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureHostConfiguration(config => + { + config.AddJsonFile("appsettings.json", false, true); + }) + ; + + var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + Console.WriteLine($"EnvironmentName={environment.EnvironmentName}"); + } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json index da93d1bc..db5d9362 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json @@ -2,9 +2,9 @@ "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" }, - "Player": { "AppId": "player1", "Key": "1234567890" - } + }, + "Environment": "Development" } \ No newline at end of file From 494af98cece0d27a03a58f3ff568b3e8ebb83f2e Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Tue, 6 Apr 2021 18:00:48 +0800 Subject: [PATCH 032/267] refactor --- Host/ConsoleAppNet5/Program.cs | 4 +- Host/ConsoleAppNetFx48/AppHost.cs | 2 +- .../ConsoleAppNetFx48/LabBackgroundService.cs | 41 +++++++++++++++-- Host/ConsoleAppNetFx48/LabHostedService.cs | 44 ++++++++++--------- Host/ConsoleAppNetFx48/Program.cs | 42 ++++++++++++++++-- Host/NetFx48/AppHost.cs | 2 +- Host/NetFx48/LabHostedService.cs | 2 +- Host/NetFx48/Program.cs | 4 +- 8 files changed, 107 insertions(+), 34 deletions(-) diff --git a/Host/ConsoleAppNet5/Program.cs b/Host/ConsoleAppNet5/Program.cs index c6aa3ecd..5c83e5b0 100644 --- a/Host/ConsoleAppNet5/Program.cs +++ b/Host/ConsoleAppNet5/Program.cs @@ -2,9 +2,9 @@ namespace ConsoleAppNet5 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { Console.WriteLine("Hello World!"); } diff --git a/Host/ConsoleAppNetFx48/AppHost.cs b/Host/ConsoleAppNetFx48/AppHost.cs index 23d90627..aa205ec7 100644 --- a/Host/ConsoleAppNetFx48/AppHost.cs +++ b/Host/ConsoleAppNetFx48/AppHost.cs @@ -8,8 +8,8 @@ namespace ConsoleAppNetFx48 { public class AppHost : IHostedService { - private readonly ILogger logger; private readonly IHostApplicationLifetime appLifetime; + private readonly ILogger logger; public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) { diff --git a/Host/ConsoleAppNetFx48/LabBackgroundService.cs b/Host/ConsoleAppNetFx48/LabBackgroundService.cs index 6436c681..56ea7423 100644 --- a/Host/ConsoleAppNetFx48/LabBackgroundService.cs +++ b/Host/ConsoleAppNetFx48/LabBackgroundService.cs @@ -9,12 +9,22 @@ namespace ConsoleAppNetFx48 public class LabBackgroundService : BackgroundService { private readonly ILogger _logger; - - public LabBackgroundService(ILogger logger) + + public LabBackgroundService(ILogger logger, + IHostApplicationLifetime appLifetime, + IHostLifetime hostLifetime, + IHostEnvironment hostEnvironment) { this._logger = logger; + appLifetime.ApplicationStarted.Register(this.OnStarted); + appLifetime.ApplicationStopping.Register(this.OnStopping); + appLifetime.ApplicationStopped.Register(this.OnStopped); + this._logger.LogInformation($"主機環境:" + + $"ApplicationName = {hostEnvironment.ApplicationName}\r\n" + + $"EnvironmentName = {hostEnvironment.EnvironmentName}\r\n" + + $"RootPath = {hostEnvironment.ContentRootPath}\r\n" + + $"Root File Provider = {hostEnvironment.ContentRootFileProvider}\r\n"); } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) @@ -23,5 +33,30 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) await Task.Delay(1000, stoppingToken); } } + + public Task StartAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("1. 調用 Host.StartAsync "); + return Task.CompletedTask; + } + private void OnStarted() + { + this._logger.LogInformation("2. 調用 OnStarted"); + } + private void OnStopping() + { + this._logger.LogInformation("3. 調用 OnStopping"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this._logger.LogInformation("4. 調用 Host.StopAsync"); + return Task.CompletedTask; + } + + private void OnStopped() + { + this._logger.LogInformation("5. 調用 OnStopped"); + } } } \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/LabHostedService.cs b/Host/ConsoleAppNetFx48/LabHostedService.cs index f97f2467..4ad14be1 100644 --- a/Host/ConsoleAppNetFx48/LabHostedService.cs +++ b/Host/ConsoleAppNetFx48/LabHostedService.cs @@ -10,42 +10,44 @@ public class LabHostedService : IHostedService private readonly ILogger _logger; public LabHostedService(ILogger logger, - IHostApplicationLifetime lifetime) + IHostApplicationLifetime appLifetime, + IHostLifetime hostLifetime, + IHostEnvironment hostEnvironment) { this._logger = logger; - - lifetime.ApplicationStarted.Register(this.OnStarted); - lifetime.ApplicationStopping.Register(this.OnStopping); - lifetime.ApplicationStopped.Register(this.OnStopped); + appLifetime.ApplicationStarted.Register(this.OnStarted); + appLifetime.ApplicationStopping.Register(this.OnStopping); + appLifetime.ApplicationStopped.Register(this.OnStopped); + this._logger.LogInformation($"主機環境:" + + $"ApplicationName = {hostEnvironment.ApplicationName}\r\n" + + $"EnvironmentName = {hostEnvironment.EnvironmentName}\r\n" + + $"RootPath = {hostEnvironment.ContentRootPath}\r\n" + + $"Root File Provider = {hostEnvironment.ContentRootFileProvider}\r\n"); } - + public Task StartAsync(CancellationToken cancellationToken) { - this._logger.LogInformation("1. StartAsync has been called."); - + this._logger.LogInformation("1. 調用 Host.StartAsync "); return Task.CompletedTask; } - - public Task StopAsync(CancellationToken cancellationToken) + private void OnStarted() { - this._logger.LogInformation("4. StopAsync has been called."); - - return Task.CompletedTask; + this._logger.LogInformation("2. 調用 OnStarted"); } - - private void OnStarted() + private void OnStopping() { - this._logger.LogInformation("2. OnStarted has been called."); + this._logger.LogInformation("3. 調用 OnStopping"); } - private void OnStopped() + public Task StopAsync(CancellationToken cancellationToken) { - this._logger.LogInformation("5. OnStopped has been called."); + this._logger.LogInformation("4. 調用 Host.StopAsync"); + return Task.CompletedTask; } - - private void OnStopping() + + private void OnStopped() { - this._logger.LogInformation("3. OnStopping has been called."); + this._logger.LogInformation("5. 調用 OnStopped"); } } } \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/Program.cs b/Host/ConsoleAppNetFx48/Program.cs index 1c21da12..0bb61d7a 100644 --- a/Host/ConsoleAppNetFx48/Program.cs +++ b/Host/ConsoleAppNetFx48/Program.cs @@ -1,12 +1,48 @@ using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace ConsoleAppNetFx48 { - class Program + // internal class Program1 + // { + // private static void Main(string[] args) + // { + // var hostBuilder = Host.CreateDefaultBuilder(args) + // .ConfigureServices((hostBuilder, services) => + // { + // services.AddHostedService(); + // Console.WriteLine($"注入 {nameof(LabHostedService)}"); + // }); + // var host = hostBuilder.Build(); + // host.RunAsync(); + // Console.WriteLine($"{nameof(LabHostedService)} 應用程式已啟動"); + // Console.ReadLine(); + // } + // } + + internal class Program { - static void Main(string[] args) + private static Task Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + var task = host.RunAsync(); + Console.WriteLine($"{nameof(LabHostedService)} 應用程式已啟動"); + + return task; + } + + private static IHostBuilder CreateHostBuilder(string[] args) { - Console.WriteLine("Hello World!"); + return Host.CreateDefaultBuilder(args) + .ConfigureServices((hostBuilder, services) => + { + services.AddHostedService(); + services.AddHostedService(); + Console.WriteLine("注入HostService"); + }) + ; } } } \ No newline at end of file diff --git a/Host/NetFx48/AppHost.cs b/Host/NetFx48/AppHost.cs index 04545531..898c3be4 100644 --- a/Host/NetFx48/AppHost.cs +++ b/Host/NetFx48/AppHost.cs @@ -8,8 +8,8 @@ namespace NetFx48 { public class AppHost : IHostedService { - private readonly ILogger logger; private readonly IHostApplicationLifetime appLifetime; + private readonly ILogger logger; public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) { diff --git a/Host/NetFx48/LabHostedService.cs b/Host/NetFx48/LabHostedService.cs index a573516d..92cec54d 100644 --- a/Host/NetFx48/LabHostedService.cs +++ b/Host/NetFx48/LabHostedService.cs @@ -13,7 +13,7 @@ public LabHostedService(ILogger logger, IHostApplicationLifetime lifetime) { this._logger = logger; - + lifetime.ApplicationStarted.Register(this.OnStarted); lifetime.ApplicationStopping.Register(this.OnStopping); lifetime.ApplicationStopped.Register(this.OnStopped); diff --git a/Host/NetFx48/Program.cs b/Host/NetFx48/Program.cs index b484980b..9aeab0ca 100644 --- a/Host/NetFx48/Program.cs +++ b/Host/NetFx48/Program.cs @@ -2,9 +2,9 @@ namespace NetFx48 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { Console.WriteLine("Hello World!"); } From 209d3e53135e49dc42e5f2fc46feab74bac83f8b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 10:55:16 +0800 Subject: [PATCH 033/267] refactor --- Host/ConsoleAppNetFx48/LabHostedService.cs | 1 + Host/ConsoleAppNetFx48/Program.cs | 1 + Host/Lab.MsHost.sln | 12 ------------ 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/Host/ConsoleAppNetFx48/LabHostedService.cs b/Host/ConsoleAppNetFx48/LabHostedService.cs index 4ad14be1..78670489 100644 --- a/Host/ConsoleAppNetFx48/LabHostedService.cs +++ b/Host/ConsoleAppNetFx48/LabHostedService.cs @@ -23,6 +23,7 @@ public LabHostedService(ILogger logger, $"EnvironmentName = {hostEnvironment.EnvironmentName}\r\n" + $"RootPath = {hostEnvironment.ContentRootPath}\r\n" + $"Root File Provider = {hostEnvironment.ContentRootFileProvider}\r\n"); + } public Task StartAsync(CancellationToken cancellationToken) diff --git a/Host/ConsoleAppNetFx48/Program.cs b/Host/ConsoleAppNetFx48/Program.cs index 0bb61d7a..50d78f7d 100644 --- a/Host/ConsoleAppNetFx48/Program.cs +++ b/Host/ConsoleAppNetFx48/Program.cs @@ -28,6 +28,7 @@ private static Task Main(string[] args) { var host = CreateHostBuilder(args).Build(); var task = host.RunAsync(); + host.WaitForShutdownAsync(); Console.WriteLine($"{nameof(LabHostedService)} 應用程式已啟動"); return task; diff --git a/Host/Lab.MsHost.sln b/Host/Lab.MsHost.sln index ea780bc4..e2e6abce 100644 --- a/Host/Lab.MsHost.sln +++ b/Host/Lab.MsHost.sln @@ -1,9 +1,5 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNet5", "ConsoleAppNet5\ConsoleAppNet5.csproj", "{1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFx48", "NetFx48\NetFx48.csproj", "{B565ABD2-BC7D-44B4-8202-EDD30B2FB260}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNetFx48", "ConsoleAppNetFx48\ConsoleAppNetFx48.csproj", "{45E650BE-BBBF-4060-B2CB-049C45B6830D}" EndProject Global @@ -12,14 +8,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CE278B8-A6DB-422C-B3CD-151A1C5D3B4E}.Release|Any CPU.Build.0 = Release|Any CPU - {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B565ABD2-BC7D-44B4-8202-EDD30B2FB260}.Release|Any CPU.Build.0 = Release|Any CPU {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Debug|Any CPU.Build.0 = Debug|Any CPU {45E650BE-BBBF-4060-B2CB-049C45B6830D}.Release|Any CPU.ActiveCfg = Release|Any CPU From 6797d1610fc740b928e4ce485a5d49ec65976630 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 11:01:28 +0800 Subject: [PATCH 034/267] refactor --- Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj index bd4231b7..aded3afa 100644 --- a/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj +++ b/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net48 From 7d10a3f7659545b26f81509be049caf4d517e107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Wed, 7 Apr 2021 11:03:01 +0800 Subject: [PATCH 035/267] delete --- Host/ConsoleAppNet48/AppHost.cs | 35 -------------- Host/ConsoleAppNet48/ConsoleAppNet48.csproj | 12 ----- Host/ConsoleAppNet48/LabBackgroundService.cs | 27 ----------- Host/ConsoleAppNet48/LabHostedService.cs | 51 -------------------- Host/ConsoleAppNet48/Program.cs | 22 --------- 5 files changed, 147 deletions(-) delete mode 100644 Host/ConsoleAppNet48/AppHost.cs delete mode 100644 Host/ConsoleAppNet48/ConsoleAppNet48.csproj delete mode 100644 Host/ConsoleAppNet48/LabBackgroundService.cs delete mode 100644 Host/ConsoleAppNet48/LabHostedService.cs delete mode 100644 Host/ConsoleAppNet48/Program.cs diff --git a/Host/ConsoleAppNet48/AppHost.cs b/Host/ConsoleAppNet48/AppHost.cs deleted file mode 100644 index 8e713810..00000000 --- a/Host/ConsoleAppNet48/AppHost.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class AppHost : IHostedService - { - private readonly ILogger logger; - private readonly IHostApplicationLifetime appLifetime; - - public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) - { - this.logger = logger; - this.appLifetime = appLifetime; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); - - await Task.Yield(); - - this.appLifetime.StopApplication(); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/ConsoleAppNet48.csproj b/Host/ConsoleAppNet48/ConsoleAppNet48.csproj deleted file mode 100644 index 50724ce9..00000000 --- a/Host/ConsoleAppNet48/ConsoleAppNet48.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - net48 - - - - - - - diff --git a/Host/ConsoleAppNet48/LabBackgroundService.cs b/Host/ConsoleAppNet48/LabBackgroundService.cs deleted file mode 100644 index ccca500a..00000000 --- a/Host/ConsoleAppNet48/LabBackgroundService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class LabBackgroundService : BackgroundService - { - private readonly ILogger _logger; - - public LabBackgroundService(ILogger logger) - { - this._logger = logger; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - await Task.Delay(1000, stoppingToken); - } - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/LabHostedService.cs b/Host/ConsoleAppNet48/LabHostedService.cs deleted file mode 100644 index b580eb10..00000000 --- a/Host/ConsoleAppNet48/LabHostedService.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class LabHostedService : IHostedService - { - private readonly ILogger _logger; - - public LabHostedService(ILogger logger, - IHostApplicationLifetime lifetime) - { - this._logger = logger; - - lifetime.ApplicationStarted.Register(this.OnStarted); - lifetime.ApplicationStopping.Register(this.OnStopping); - lifetime.ApplicationStopped.Register(this.OnStopped); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("1. StartAsync has been called."); - - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("4. StopAsync has been called."); - - return Task.CompletedTask; - } - - private void OnStarted() - { - this._logger.LogInformation("2. OnStarted has been called."); - } - - private void OnStopped() - { - this._logger.LogInformation("5. OnStopped has been called."); - } - - private void OnStopping() - { - this._logger.LogInformation("3. OnStopping has been called."); - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/Program.cs b/Host/ConsoleAppNet48/Program.cs deleted file mode 100644 index ee76ac29..00000000 --- a/Host/ConsoleAppNet48/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace ConsoleAppNet48 -{ - public class Program - { - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureServices((hostContext, services) => - { - services.AddHostedService(); - }); - } - - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - } -} \ No newline at end of file From 49060369d5d508bf46c485eb2429f2595e7894be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Wed, 7 Apr 2021 11:03:15 +0800 Subject: [PATCH 036/267] delete --- Host/ConsoleAppNet5/ConsoleAppNet5.csproj | 8 -------- Host/ConsoleAppNet5/Program.cs | 12 ------------ 2 files changed, 20 deletions(-) delete mode 100644 Host/ConsoleAppNet5/ConsoleAppNet5.csproj delete mode 100644 Host/ConsoleAppNet5/Program.cs diff --git a/Host/ConsoleAppNet5/ConsoleAppNet5.csproj b/Host/ConsoleAppNet5/ConsoleAppNet5.csproj deleted file mode 100644 index 9590466a..00000000 --- a/Host/ConsoleAppNet5/ConsoleAppNet5.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net5.0 - - - diff --git a/Host/ConsoleAppNet5/Program.cs b/Host/ConsoleAppNet5/Program.cs deleted file mode 100644 index 5c83e5b0..00000000 --- a/Host/ConsoleAppNet5/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ConsoleAppNet5 -{ - internal class Program - { - private static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} \ No newline at end of file From b2b4931047b86baad63bd360abd5d6bfb795807a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Wed, 7 Apr 2021 11:03:26 +0800 Subject: [PATCH 037/267] delete --- Host/NetFx48/AppHost.cs | 35 ------------------- Host/NetFx48/LabBackgroundService.cs | 27 --------------- Host/NetFx48/LabHostedService.cs | 51 ---------------------------- Host/NetFx48/NetFx48.csproj | 11 ------ Host/NetFx48/Program.cs | 12 ------- 5 files changed, 136 deletions(-) delete mode 100644 Host/NetFx48/AppHost.cs delete mode 100644 Host/NetFx48/LabBackgroundService.cs delete mode 100644 Host/NetFx48/LabHostedService.cs delete mode 100644 Host/NetFx48/NetFx48.csproj delete mode 100644 Host/NetFx48/Program.cs diff --git a/Host/NetFx48/AppHost.cs b/Host/NetFx48/AppHost.cs deleted file mode 100644 index 898c3be4..00000000 --- a/Host/NetFx48/AppHost.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace NetFx48 -{ - public class AppHost : IHostedService - { - private readonly IHostApplicationLifetime appLifetime; - private readonly ILogger logger; - - public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) - { - this.logger = logger; - this.appLifetime = appLifetime; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); - - await Task.Yield(); - - this.appLifetime.StopApplication(); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Host/NetFx48/LabBackgroundService.cs b/Host/NetFx48/LabBackgroundService.cs deleted file mode 100644 index 69048bdd..00000000 --- a/Host/NetFx48/LabBackgroundService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace NetFx48 -{ - public class LabBackgroundService : BackgroundService - { - private readonly ILogger _logger; - - public LabBackgroundService(ILogger logger) - { - this._logger = logger; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - await Task.Delay(1000, stoppingToken); - } - } - } -} \ No newline at end of file diff --git a/Host/NetFx48/LabHostedService.cs b/Host/NetFx48/LabHostedService.cs deleted file mode 100644 index 92cec54d..00000000 --- a/Host/NetFx48/LabHostedService.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace NetFx48 -{ - public class LabHostedService : IHostedService - { - private readonly ILogger _logger; - - public LabHostedService(ILogger logger, - IHostApplicationLifetime lifetime) - { - this._logger = logger; - - lifetime.ApplicationStarted.Register(this.OnStarted); - lifetime.ApplicationStopping.Register(this.OnStopping); - lifetime.ApplicationStopped.Register(this.OnStopped); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("1. StartAsync has been called."); - - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("4. StopAsync has been called."); - - return Task.CompletedTask; - } - - private void OnStarted() - { - this._logger.LogInformation("2. OnStarted has been called."); - } - - private void OnStopped() - { - this._logger.LogInformation("5. OnStopped has been called."); - } - - private void OnStopping() - { - this._logger.LogInformation("3. OnStopping has been called."); - } - } -} \ No newline at end of file diff --git a/Host/NetFx48/NetFx48.csproj b/Host/NetFx48/NetFx48.csproj deleted file mode 100644 index bd4231b7..00000000 --- a/Host/NetFx48/NetFx48.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - Exe - net5.0 - - - - - - diff --git a/Host/NetFx48/Program.cs b/Host/NetFx48/Program.cs deleted file mode 100644 index 9aeab0ca..00000000 --- a/Host/NetFx48/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace NetFx48 -{ - internal class Program - { - private static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} \ No newline at end of file From 6365f1979bad6b0e90bc00667483f918c56eec0d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 13:57:35 +0800 Subject: [PATCH 038/267] add Lab.WorkerService --- .../2021-03-26_16-34-47_CoverageHistory.xml | 12 + .../report/ClassLibrary1_Calculation.htm | 82 ++ .../Lab.OpenCoverDemo/report/Cobertura.xml | 105 ++ .../report/UnitTestProject1_UnitTest1.htm | 88 ++ .../report/UnitTestProject2_UnitTest1.htm | 75 + .../report/badge_linecoverage.png | Bin 0 -> 2925 bytes .../report/badge_linecoverage.svg | 88 ++ .../Lab.OpenCoverDemo/report/class.js | 207 +++ .../Lab.OpenCoverDemo/report/coverage.xml | 1270 +++++++++++++++++ .../Lab.OpenCoverDemo/report/icon_cube.svg | 2 + .../report/icon_down-dir_active.svg | 2 + .../Lab.OpenCoverDemo/report/icon_fork.svg | 2 + .../report/icon_info-circled.svg | 2 + .../Lab.OpenCoverDemo/report/icon_minus.svg | 2 + .../Lab.OpenCoverDemo/report/icon_plus.svg | 2 + .../report/icon_search-minus.svg | 2 + .../report/icon_search-plus.svg | 2 + .../Lab.OpenCoverDemo/report/icon_up-dir.svg | 2 + .../report/icon_up-dir_active.svg | 2 + .../Lab.OpenCoverDemo/report/icon_wrench.svg | 2 + .../Lab.OpenCoverDemo/report/index.htm | 71 + .../Lab.OpenCoverDemo/report/main.js | 274 ++++ .../Lab.OpenCoverDemo/report/report.css | 357 +++++ .../Lab.Config/ConsoleApp1/ConsoleApp1.csproj | 0 .../Lab.Config/ConsoleApp1}/Program.cs | 6 +- .../Properties/launchSettings.json | 8 + .../NetFx48/Properties/launchSettings.json | 8 + .../ConsoleApp1/ConsoleApp1.csproj | 3 - .../ConsoleApp1}/Program.cs | 6 +- .../WebApi.NetFx48.csproj.DotSettings | 2 + Host/ConsoleAppNet48/AppHost.cs | 35 - Host/ConsoleAppNet48/ConsoleAppNet48.csproj | 12 - Host/ConsoleAppNet48/LabBackgroundService.cs | 27 - Host/ConsoleAppNet48/LabHostedService.cs | 51 - Host/ConsoleAppNet48/Program.cs | 22 - .../ConsoleAppNetFx48/AppHost.cs | 0 .../ConsoleAppNetFx48.csproj | 0 .../ConsoleAppNetFx48/LabBackgroundService.cs | 0 .../ConsoleAppNetFx48/LabHostedService.cs | 0 .../ConsoleAppNetFx48/Program.cs | 0 Host/{ => Lab.MsHost}/Lab.MsHost.sln | 0 .../ConsoleAppNetFx48.csproj | 17 + .../ConsoleAppNetFx48/DoThing.cs | 38 + .../ConsoleAppNetFx48/Program.cs | 38 + .../Properties/launchSettings.json | 11 + .../ConsoleAppNetFx48/Worker.cs} | 10 +- .../appsettings.Development.json | 9 + .../ConsoleAppNetFx48/appsettings.json | 9 + Host/Lab.WorkerService/Lab.WorkerService.sln | 16 + Host/NetFx48/AppHost.cs | 35 - Host/NetFx48/LabHostedService.cs | 51 - .../EntityModel/CopyMe.SqlServer.generated.cs | 216 +++ 52 files changed, 3034 insertions(+), 247 deletions(-) create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js create mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/report.css rename Host/ConsoleAppNet5/ConsoleAppNet5.csproj => Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj (100%) rename {Host/NetFx48 => Configuration/NetCore/Lab.Config/ConsoleApp1}/Program.cs (51%) create mode 100644 Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/Properties/launchSettings.json rename Host/NetFx48/NetFx48.csproj => Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj (59%) rename {Host/ConsoleAppNet5 => Coravel/Lab.CoravelScheduler/ConsoleApp1}/Program.cs (50%) create mode 100644 Coravel/Lab.CoravelScheduler/WebApi.NetFx48/WebApi.NetFx48.csproj.DotSettings delete mode 100644 Host/ConsoleAppNet48/AppHost.cs delete mode 100644 Host/ConsoleAppNet48/ConsoleAppNet48.csproj delete mode 100644 Host/ConsoleAppNet48/LabBackgroundService.cs delete mode 100644 Host/ConsoleAppNet48/LabHostedService.cs delete mode 100644 Host/ConsoleAppNet48/Program.cs rename Host/{ => Lab.MsHost}/ConsoleAppNetFx48/AppHost.cs (100%) rename Host/{ => Lab.MsHost}/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj (100%) rename Host/{ => Lab.MsHost}/ConsoleAppNetFx48/LabBackgroundService.cs (100%) rename Host/{ => Lab.MsHost}/ConsoleAppNetFx48/LabHostedService.cs (100%) rename Host/{ => Lab.MsHost}/ConsoleAppNetFx48/Program.cs (100%) rename Host/{ => Lab.MsHost}/Lab.MsHost.sln (100%) create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/DoThing.cs create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/Properties/launchSettings.json rename Host/{NetFx48/LabBackgroundService.cs => Lab.WorkerService/ConsoleAppNetFx48/Worker.cs} (70%) create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.Development.json create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json create mode 100644 Host/Lab.WorkerService/Lab.WorkerService.sln delete mode 100644 Host/NetFx48/AppHost.cs delete mode 100644 Host/NetFx48/LabHostedService.cs create mode 100644 ORM/Linq2Db/Lab.Linq2Db/Lab.UnitTest/EntityModel/CopyMe.SqlServer.generated.cs diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml new file mode 100644 index 00000000..7531ba24 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm new file mode 100644 index 00000000..6976b753 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm @@ -0,0 +1,82 @@ + + + + + + +ClassLibrary1.Calculation - Coverage Report + +
+

Summary

+ ++++ + + + + + + + + + + + + + +
Class:ClassLibrary1.Calculation
Assembly:ClassLibrary1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs
Covered lines:3
Uncovered lines:6
Coverable lines:9
Total lines:18
Line coverage:33.3% (3 of 9)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
+

Coverage History

+
+ +

Metrics

+ + + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
Add(...)10100%100%1
Sub(...)100%0%2
Sub1(...)100%0%2
+

File(s)

+

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs

+ + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1namespace ClassLibrary1
 2{
 3    public class Calculation
 4    {
 5        public int Add(int firstNumber, int secondNumber)
 26        {
 27            return firstNumber + secondNumber;
 28        }
 9        public int Sub(int firstNumber, int secondNumber)
 010        {
 011            return firstNumber + secondNumber;
 012        }
 13        public int Sub1(int firstNumber, int secondNumber)
 014        {
 015            return firstNumber + secondNumber;
 016        }
 17    }
 18}
+
+
+ + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml new file mode 100644 index 00000000..105f9442 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm new file mode 100644 index 00000000..1ec7556c --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm @@ -0,0 +1,88 @@ + + + + + + +UnitTestProject1.UnitTest1 - Coverage Report + +
+

Summary

+ ++++ + + + + + + + + + + + + + +
Class:UnitTestProject1.UnitTest1
Assembly:UnitTestProject1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs
Covered lines:9
Uncovered lines:1
Coverable lines:10
Total lines:26
Line coverage:90% (9 of 10)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
+

Coverage History

+
+ +

Metrics

+ + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
TestMethod2()1080%100%1.01
+

File(s)

+

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using System;
 2using ClassLibrary1;
 3using Microsoft.VisualStudio.TestTools.UnitTesting;
 4
 5namespace UnitTestProject1
 6{
 7    [TestClass]
 8    public class UnitTest1
 9    {
 10        [TestMethod]
 11        public void TestMethod1()
 112        {
 113            var calculation = new Calculation();
 114            var actual = calculation.Add(1, 1);
 115            Assert.AreEqual(2,actual);
 116        }
 17
 18        [TestMethod]
 19        public void TestMethod2()
 120        {
 121            var calculation = new Calculation();
 122            var actual = calculation.Add(1, 1);
 123            Assert.AreEqual(1,actual);
 024        }
 25    }
 26}
+
+
+
+

Methods/Properties

+TestMethod1()
+TestMethod2()
+
+
+ + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm new file mode 100644 index 00000000..7d13ebe2 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm @@ -0,0 +1,75 @@ + + + + + + +UnitTestProject2.UnitTest1 - Coverage Report + +
+

Summary

+ ++++ + + + + + + + + + + + + + +
Class:UnitTestProject2.UnitTest1
Assembly:UnitTestProject2
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs
Covered lines:3
Uncovered lines:0
Coverable lines:3
Total lines:15
Line coverage:100% (3 of 3)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
+

Coverage History

+
+ +

Metrics

+ + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
+

File(s)

+

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs

+ + + + + + + + + + + + + + + + + + + +
#LineLine coverage
 1using System;
 2using Microsoft.VisualStudio.TestTools.UnitTesting;
 3
 4namespace UnitTestProject2
 5{
 6    [TestClass]
 7    public class UnitTest1
 8    {
 9        [TestMethod]
 10        public void TestMethod1()
 111        {
 112            Assert.AreEqual(1,1);
 113        }
 14    }
 15}
+
+
+
+

Methods/Properties

+TestMethod1()
+
+
+ + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png new file mode 100644 index 0000000000000000000000000000000000000000..1d4585190f78c440e6cda1f35623d1780d05fbd8 GIT binary patch literal 2925 zcmV-z3zGDSP)4*Bbt2V+UCULEJzD1zd5%y@;aL*s6%J_)O5c zMH`=0TcWmr#>6Ey#ywUOHBGv>)C~+G&jQL5P+Ursry}?e*})NkVU~CPgLj+-hM}U3 zPk(a4%vt{P-+R9M{ogrvq!AGjmKGKk2>`xer3*gVTd~-*v<~$bajZk#y-h0-L3FIL zu5yD^CX*#-G@3PkcSQg1#_n+k`|D75Z_~PHpjqo=fNZ6RKvN_0bT{2VfWnnT?YML+XoCbZVYh$Ah3BDHi%g1!vHokJRD-NSX-}F zYan;3hupg!h5bw6mG1_>Y;Q=LB=0ir_tHLeurc$H8r}pk`~LZzJLrDx8cN5HhrP42 zwoUp%3YmpW`@Jlv1`Qqx1fT5%$w$DMw2xq8*fOjb_vjIZJbMPW=g%SCy&IO6maw(8 z{fhxr>Vn*yiMoFXE#y+OYD#fq@5bcj8?f9()*{)}Gq7FMEE8 z%VRDhd2}+OyS1vCxLJrTaANog_}lwy>!x~4Mc%|bq>N5MZ@b>w`t-5s7~wL)>=^Z! z&85B`lZuKUku2n;rQ3W`@ib${yzSScZdxfxp+E2_P3jFW_YHNlZ?kjBopV z%Yip7V;Uy8PeN>;SbTQvGswg;oE~u+1$6~Tt1#XjJ6ky8(BMM|>l}vf^1jo?9N#w{ zk^hcFh*Jnw`L4p!n@cg!aUk3*-Eg(ys@d0|2ex61(JVzwu}NO1#6kP_qhRo0_yz|< z%7E4q*vKk}*1`T!+Z%=@OP0XN$qA7Rz%O3BU^-BZ$&)9;-rin&an{z>oKXpk<{E*x zQ>RX-sHiZRvgko_badqNl7GX#&P+BmHa4QJuFhyI#jQ{%T5M~}4ChH_*ZU@>>u!SE z1k|hQk&u@_LL9MoV-e~S%IEa6^FwY;E^?}Kc;DRGTm;w$;E(b@^!(XKY;b+Tb^KWH zBZfH-BSo)`+1Jh&g>{ASu=K#ET|VXYKM(sEk(rV0k_UQV3uZT8Dv=m=fYbNiL;1@u ztc@|7tYxq%d{j_NG!Q9UpUigs9^K=MgIr+eb+>WcmBK}$_dg{P+{)~#EI z=ZuZq+}x0zosC0>4w-$E$=;@B8{jwqSyfs1#{U~k?=l^kRhfv-i|2EmEmabj9qN=J&%M3z2fs8;* zfT2FKX3fI<`SX#Lm4$ip=Ao>t4A-w;$Cxo=czTOf+8bPk^kftkn7>@D3k7oHH41 zdM+|eN__(Z1NmG6+X~|;?e^=}53^^_=Bb~I#(8;pp`f6k)oV4nHwM=`_T@{n$C6#K z(bvPe?g%`0Ywu#ioelhZUTq$VrWL_a=7?><+mKwEjJQAJc-!Wn&Dh?5JEE>dnT{8) z*Rry)5}Wfj^Uo9AC*rp!zoA}PkHj&F*nWRIlEx%q*rj2pZK^ftQx9x4R#xcB^opU? zvXrwkt}CQ=CXP4MY+VmslfAQmEk^ytw8;o;#N z7{VB0q!TAjG~XoFmo%65X=46_vqECE(9lqxIw_SdSg=43n8dKU@$06Nef#!trX-{E zF3e%B8TS2`^_v}t>$5(x1`LI18YRs`jI?UiD*kptsHgNusVO%%7o$gy=5ta#eZFzyMsqq~b*E09;;D?7 zic%ug=j7yI#E20b0HnQWj-EYxwg?(Zzj=9iTn~~a^Y!)Bw$nT_X3XH=>)W?4*LXa= zuvB{c_H7;u`KLncNxuH>^eL1x#b@Ach$ViWF8s}jvaXT@FCY= zB)$m{4Gj%kmg zmKugF{kPy?$U&U`?`cG5M)Ns0{ke&!#H6uFP-#>+`}8cf6YOhlj}rX%(ukWu<1_8=CaBM~7D zQp%({>PrhQ*h{8=2pHs#07U&sSJ6(CKqd4gpio}|BrzlTqq*q4FMSr`qA|yhALjt0 zbW7tXE-NN8C@w(*N?OskEt%o3K0bK`WOFUWKr6rNUwshRY$fcTD&>0hbeR0C;h9-J zj9ynAba6p1c5}U%KG_!M1F?q{OrG)AYM|Wl0-7eZ*?|)f$Y|kdv4u?s#X@cLpY)L+ z>V+7I8J%CfKGThx06>NO(Rk`d?F35Qz5)nq8G8$DLOX35=&+f<4xhj|&d00000NkvXXu0mjf72v4t literal 0 HcmV?d00001 diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg new file mode 100644 index 00000000..f3f4a54c --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg @@ -0,0 +1,88 @@ + + + Code coverage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Generated by: ReportGenerator 4.1.2.0 + + + + Coverage + Coverage + 68.1%68.1% + + + + + Line coverage + + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js new file mode 100644 index 00000000..b82bca96 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js @@ -0,0 +1,207 @@ +/* Chartist.js 0.11.0 + * Copyright © 2017 Gion Kunz + * Free to use under either the WTFPL license or the MIT license. + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT + */ + +!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { + var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { + "use strict"; function d(a) { + var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { + var i = c.serialMap(b.normalized.series, function () { + return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) + }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") + } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) + } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) + }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a +}); + +var i, l, selectedLine = null; + +/* Navigate to hash without browser history entry */ +var navigateToHash = function () { + if (window.history !== undefined && window.history.replaceState !== undefined) { + window.history.replaceState(undefined, undefined, this.getAttribute("href")); + } +}; + +var hashLinks = document.getElementsByClassName('navigatetohash'); +for (i = 0, l = hashLinks.length; i < l; i++) { + hashLinks[i].addEventListener('click', navigateToHash); +} + +/* Switch test method */ +var switchTestMethod = function () { + var method = this.getAttribute("value"); + console.log("Selected test method: " + method); + + var lines, i, l, coverageData, lineAnalysis, cells; + + lines = document.querySelectorAll('.lineAnalysis tr'); + + for (i = 1, l = lines.length; i < l; i++) { + coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); + lineAnalysis = coverageData[method]; + cells = lines[i].querySelectorAll('td'); + if (lineAnalysis === undefined) { + lineAnalysis = coverageData.AllTestMethods; + if (lineAnalysis.LVS !== 'gray') { + cells[0].setAttribute('class', 'red'); + cells[1].innerText = cells[1].textContent = '0'; + cells[4].setAttribute('class', 'lightred'); + } + } else { + cells[0].setAttribute('class', lineAnalysis.LVS); + cells[1].innerText = cells[1].textContent = lineAnalysis.VC; + cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); + } + } +}; + +var testMethods = document.getElementsByClassName('switchtestmethod'); +for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].addEventListener('change', switchTestMethod); +} + +/* Highlight test method by line */ +var toggleLine = function () { + if (selectedLine === this) { + selectedLine = null; + } else { + selectedLine = null; + unhighlightTestMethods(); + highlightTestMethods.call(this); + selectedLine = this; + } + +}; +var highlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var lineAnalysis; + var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); + var testMethods = document.getElementsByClassName('testmethod'); + + for (i = 0, l = testMethods.length; i < l; i++) { + lineAnalysis = coverageData[testMethods[i].id]; + if (lineAnalysis === undefined) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } else { + testMethods[i].className += ' light' + lineAnalysis.LVS; + } + } +}; +var unhighlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var testMethods = document.getElementsByClassName('testmethod'); + for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } +}; +var coverableLines = document.getElementsByClassName('coverableline'); +for (i = 0, l = coverableLines.length; i < l; i++) { + coverableLines[i].addEventListener('click', toggleLine); + coverableLines[i].addEventListener('mouseenter', highlightTestMethods); + coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); +} + +/* History charts */ +var renderChart = function (chart) { + // Remove current children (e.g. PNG placeholder) + while (chart.firstChild) { + chart.firstChild.remove(); + } + + var chartData = window[chart.getAttribute('data-data')]; + var options = { + axisY: { + type: undefined, + onlyInteger: true + }, + lineSmooth: false, + low: 0, + high: 100, + scaleMinSpace: 20, + onlyInteger: true, + fullWidth: true + }; + var lineChart = new Chartist.Line(chart, { + labels: [], + series: chartData.series + }, options); + + /* Zoom */ + var zoomButtonDiv = document.createElement("div"); + zoomButtonDiv.className = "toggleZoom"; + var zoomButtonLink = document.createElement("a"); + zoomButtonLink.setAttribute("href", ""); + var zoomButtonText = document.createElement("i"); + zoomButtonText.className = "icon-search-plus"; + + zoomButtonLink.appendChild(zoomButtonText); + zoomButtonDiv.appendChild(zoomButtonLink); + + chart.appendChild(zoomButtonDiv); + + zoomButtonDiv.addEventListener('click', function (event) { + event.preventDefault(); + + if (options.axisY.type === undefined) { + options.axisY.type = Chartist.AutoScaleAxis; + zoomButtonText.className = "icon-search-minus"; + } else { + options.axisY.type = undefined; + zoomButtonText.className = "icon-search-plus"; + } + + lineChart.update(null, options); + }); + + var tooltip = document.createElement("div"); + tooltip.className = "tooltip"; + + chart.appendChild(tooltip); + + /* Tooltips */ + var showToolTip = function () { + var point = this; + var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); + + tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; + tooltip.style.display = 'block'; + }; + + var moveToolTip = function (event) { + var box = chart.getBoundingClientRect(); + var left = event.pageX - box.left - window.pageXOffset; + var top = event.pageY - box.top - window.pageYOffset; + + tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; + tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; + }; + + var hideToolTip = function () { + tooltip.style.display = 'none'; + }; + + chart.addEventListener('mousemove', moveToolTip); + + lineChart.on('created', function () { + var chartPoints = chart.getElementsByClassName('ct-point'); + for (i = 0, l = chartPoints.length; i < l; i++) { + chartPoints[i].addEventListener('mousemove', showToolTip); + chartPoints[i].addEventListener('mouseout', hideToolTip); + } + }); +}; + +var charts = document.getElementsByClassName('historychart'); +for (i = 0, l = charts.length; i < l; i++) { + renderChart(charts[i]); +} \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml new file mode 100644 index 00000000..dbfc7f84 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml @@ -0,0 +1,1270 @@ + + + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll + 2021-02-09T23:13:42Z + mscorlib + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe + 2021-03-10T01:18:39.6988681Z + vstest.console + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Client.dll + 2021-03-10T01:18:39.3608699Z + Microsoft.VisualStudio.TestPlatform.Client + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Common.dll + 2021-03-10T01:18:39.3648679Z + Microsoft.VisualStudio.TestPlatform.Common + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll + 2021-02-09T23:13:40.5754059Z + System.Core + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll + 2019-12-07T09:10:37.7737543Z + System.Xml + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.Utilities.dll + 2021-03-10T01:18:39.1838668Z + Microsoft.TestPlatform.Utilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\NuGet.Frameworks.dll + 2021-03-10T01:18:39.455872Z + NuGet.Frameworks + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll + 2020-06-05T05:04:05.335002Z + System.Configuration + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.Extensions.FileSystemGlobbing.dll + 2021-03-10T01:18:39.1398684Z + Microsoft.Extensions.FileSystemGlobbing + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CrossPlatEngine.dll + 2021-03-10T01:18:39.1798685Z + Microsoft.TestPlatform.CrossPlatEngine + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.Cci.dll + 2021-03-10T01:18:40.1568669Z + Microsoft.Cci + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll + 2019-12-07T09:10:34.4923358Z + System.Runtime + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.InteropServices\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.InteropServices.dll + 2019-12-07T09:10:35.9300981Z + System.Runtime.InteropServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll + 2019-12-07T09:10:34.5705678Z + System.Collections + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.BlameDataCollector.dll + 2021-03-10T01:18:38.7708669Z + Microsoft.TestPlatform.Extensions.BlameDataCollector + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.EventLogCollector.dll + 2021-03-10T01:18:38.7728666Z + Microsoft.TestPlatform.Extensions.EventLogCollector + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.TestHostRuntimeProvider.dll + 2021-03-10T01:18:38.7748692Z + Microsoft.TestPlatform.TestHostRuntimeProvider + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.ArchitectureTools.PEReader.dll + 2021-03-10T01:18:39.2018677Z + Microsoft.VisualStudio.ArchitectureTools.PEReader + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Coverage.Interprocess.dll + 2021-03-10T01:18:38.7888677Z + Microsoft.VisualStudio.Coverage.Interprocess + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\netstandard\v4.0_2.0.0.0__cc7b13ffcd2ddd51\netstandard.dll + 2019-12-07T09:10:37.6330785Z + netstandard + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ValueTuple\v4.0_4.0.0.0__cc7b13ffcd2ddd51\System.ValueTuple.dll + 2019-12-07T09:10:37.742541Z + System.ValueTuple + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Fakes.DataCollector.dll + 2021-03-10T01:18:40.1588666Z + Microsoft.VisualStudio.Fakes.DataCollector + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter.dll + 2021-03-10T01:18:38.7898664Z + Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter.dll + 2021-03-10T01:18:38.7968662Z + Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Common.dll + 2021-03-10T01:18:39.2388684Z + Microsoft.VisualStudio.QualityTools.Common + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter.dll + 2021-03-10T01:18:38.7918661Z + Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger.dll + 2021-03-10T01:18:38.792866Z + Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter.dll + 2021-03-10T01:18:38.7948677Z + Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger.dll + 2021-03-10T01:18:38.7998656Z + Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration.dll + 2021-03-10T01:18:38.8038671Z + Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll + 2021-03-10T01:18:39.3488657Z + Microsoft.VisualStudio.QualityTools.UnitTestFramework + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll + 2020-09-04T22:37:08Z + System.Data + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter.dll + 2021-03-10T01:18:38.8048665Z + Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces.dll + 2021-03-10T01:18:38.8068659Z + Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension.dll + 2021-03-10T01:18:38.8078663Z + Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model.dll + 2021-03-10T01:18:38.8098657Z + Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector.dll + 2021-03-10T01:18:38.8118659Z + Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TraceDataCollector.dll + 2021-03-10T01:18:38.8168685Z + Microsoft.VisualStudio.TraceDataCollector + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.IntelliTrace.Core.dll + 2021-03-10T01:18:39.1678662Z + Microsoft.IntelliTrace.Core + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Reflection.Metadata.dll + 2021-03-10T01:18:39.5908676Z + System.Reflection.Metadata + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll + 2019-12-07T09:10:34.5552678Z + System.IO + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Collections.Immutable.dll + 2021-03-10T01:18:39.5778692Z + System.Collections.Immutable + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll + 2019-12-07T09:10:36.0237545Z + System.Reflection + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.FileSystem\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.FileSystem.dll + 2019-12-07T09:10:36.0095245Z + System.IO.FileSystem + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.MemoryMappedFiles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.MemoryMappedFiles.dll + 2019-12-07T09:10:34.5552678Z + System.IO.MemoryMappedFiles + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Handles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Handles.dll + 2019-12-07T09:10:37.6643716Z + System.Runtime.Handles + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll + 2019-12-07T09:10:37.7113237Z + System.Reflection.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll + 2019-12-07T09:10:34.5860246Z + System.Threading + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.dll + 2019-12-07T09:10:36.0705812Z + System.Text.Encoding + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll + 2019-12-07T09:10:37.6174386Z + System.Runtime.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.Extensions.dll + 2019-12-07T09:10:36.1495949Z + System.Text.Encoding.Extensions + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Fakes.dll + 2021-03-10T01:18:40.1608669Z + Microsoft.VisualStudio.TestPlatform.Fakes + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CommunicationUtilities.dll + 2021-03-10T01:18:39.1748663Z + Microsoft.TestPlatform.CommunicationUtilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Newtonsoft.Json.dll + 2021-03-10T01:18:39.451869Z + Newtonsoft.Json + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + 2018-06-05T04:51:36Z + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll + 2019-12-07T09:10:37.6960843Z + System.Resources.ResourceManager + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + 2018-06-05T04:47:02Z + Microsoft.VisualStudio.TestPlatform.TestFramework + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll + 2018-06-05T04:50:06Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ComponentModel.Composition\v4.0_4.0.0.0__b77a5c561934e089\System.ComponentModel.Composition.dll + 2019-12-07T09:10:36.1023351Z + System.ComponentModel.Composition + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll + 2019-12-07T09:10:36.086204Z + System.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.ReaderWriter\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.ReaderWriter.dll + 2019-12-07T09:10:34.5705678Z + System.Xml.ReaderWriter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.XDocument\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.XDocument.dll + 2019-12-07T09:10:34.5552678Z + System.Xml.XDocument + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll + 2019-12-07T09:10:35.962301Z + System.Xml.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll + 2019-12-07T09:10:36.086204Z + System.Globalization + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\UnitTestProject1.dll + 2019-04-19T07:50:12.1584913Z + UnitTestProject1 + + + + + + + <Module> + + + + + UnitTestProject1.UnitTest1 + + + + 100663297 + System.Void UnitTestProject1.UnitTest1::TestMethod1() + + + + + + + + + + + + + + 100663298 + System.Void UnitTestProject1.UnitTest1::TestMethod2() + + + + + + + + + + + + + + 100663299 + System.Void UnitTestProject1.UnitTest1::.ctor() + + + + + + + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + 2018-06-05T04:51:36Z + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll + 2019-12-07T09:10:34.4923358Z + System.Runtime + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + 2018-06-05T04:47:02Z + Microsoft.VisualStudio.TestPlatform.TestFramework + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll + 2019-12-07T09:10:34.5705678Z + System.Collections + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll + 2018-06-05T04:50:06Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll + 2019-12-07T09:10:36.0237545Z + System.Reflection + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll + 2019-12-07T09:10:36.086204Z + System.Globalization + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll + 2019-12-07T09:10:36.086204Z + System.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll + 2021-02-09T23:13:40.5754059Z + System.Core + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll + 2019-12-07T09:10:34.5860246Z + System.Threading + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll + 2019-12-07T09:10:37.7113237Z + System.Reflection.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll + 2019-12-07T09:10:34.5705678Z + System.Threading.Tasks + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll + 2019-12-07T09:10:37.6174386Z + System.Runtime.Extensions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + 2018-06-05T04:53:44Z + Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll + 2020-08-05T02:39:55.9298663Z + System.Runtime.Serialization + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\bin\debug\UnitTestProject2.dll + 2019-04-19T07:50:12.1584913Z + UnitTestProject2 + + + + + + + <Module> + + + + + UnitTestProject2.UnitTest1 + + + + 100663297 + System.Void UnitTestProject2.UnitTest1::TestMethod1() + + + + + + + + + + + + 100663298 + System.Void UnitTestProject2.UnitTest1::.ctor() + + + + + + + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + 2018-06-05T04:51:36Z + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll + 2019-12-07T09:10:34.4923358Z + System.Runtime + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + 2018-06-05T04:47:02Z + Microsoft.VisualStudio.TestPlatform.TestFramework + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll + 2019-12-07T09:10:34.5705678Z + System.Collections + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll + 2018-06-05T04:50:06Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll + 2019-12-07T09:10:36.0237545Z + System.Reflection + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll + 2019-12-07T09:10:36.086204Z + System.Globalization + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll + 2019-12-07T09:10:36.086204Z + System.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll + 2021-02-09T23:13:40.5754059Z + System.Core + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll + 2019-12-07T09:10:34.5860246Z + System.Threading + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll + 2019-12-07T09:10:37.7113237Z + System.Reflection.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll + 2019-12-07T09:10:34.5705678Z + System.Threading.Tasks + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll + 2019-12-07T09:10:37.6174386Z + System.Runtime.Extensions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + 2018-06-05T04:53:44Z + Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll + 2020-08-05T02:39:55.9298663Z + System.Runtime.Serialization + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections.Concurrent\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.Concurrent.dll + 2019-12-07T09:10:34.5552678Z + System.Collections.Concurrent + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll + 2019-12-07T09:10:34.5705678Z + System.Threading.Tasks + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + 2018-06-05T04:51:36Z + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll + 2019-12-07T09:10:34.4923358Z + System.Runtime + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + 2018-06-05T04:47:02Z + Microsoft.VisualStudio.TestPlatform.TestFramework + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll + 2019-12-07T09:10:34.5705678Z + System.Collections + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll + 2019-12-07T09:10:36.0237545Z + System.Reflection + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll + 2019-12-07T09:10:37.6174386Z + System.Runtime.Extensions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll + 2018-06-05T04:50:06Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll + 2019-12-07T09:10:36.086204Z + System.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll + 2021-02-09T23:13:40.5754059Z + System.Core + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll + 2019-12-07T09:10:36.086204Z + System.Globalization + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll + 2019-12-07T09:10:34.5552678Z + System.IO + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + 2018-06-05T04:53:44Z + Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll + 2020-09-04T22:37:08Z + System.Data + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll + 2019-12-07T09:10:34.5860246Z + System.Threading + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll + 2019-12-07T09:10:37.7113237Z + System.Reflection.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll + 2019-12-07T09:10:34.5705678Z + System.Threading.Tasks + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll + 2020-06-05T05:04:05.335002Z + System.Configuration + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll + 2019-12-07T09:10:37.7737543Z + System.Xml + + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\ClassLibrary1.dll + 2019-04-19T06:37:47.6105603Z + ClassLibrary1 + + + + + + + <Module> + + + + + ClassLibrary1.Calculation + + + + 100663297 + System.Int32 ClassLibrary1.Calculation::Add(System.Int32,System.Int32) + + + + + + + + + + + + 100663298 + System.Int32 ClassLibrary1.Calculation::Sub(System.Int32,System.Int32) + + + + + + + + + + + + 100663299 + System.Int32 ClassLibrary1.Calculation::Sub1(System.Int32,System.Int32) + + + + + + + + + + + + 100663300 + System.Void ClassLibrary1.Calculation::.ctor() + + + + + + + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll + 2019-12-07T09:10:37.6960843Z + System.Resources.ResourceManager + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.RegularExpressions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.RegularExpressions.dll + 2019-12-07T09:10:34.4610177Z + System.Text.RegularExpressions + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll + 2021-03-10T01:18:39.1758657Z + Microsoft.TestPlatform.CoreUtilities + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll + 2021-03-10T01:18:39.1818661Z + Microsoft.TestPlatform.PlatformAbstractions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + 2018-06-05T04:54:38Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll + 2020-10-08T02:21:33.8194762Z + System + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + 2018-06-05T04:51:36Z + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll + 2019-12-07T09:10:34.4923358Z + System.Runtime + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + 2018-06-05T04:47:02Z + Microsoft.VisualStudio.TestPlatform.TestFramework + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll + 2019-12-07T09:10:34.5705678Z + System.Collections + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll + 2019-12-07T09:10:36.0237545Z + System.Reflection + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll + 2019-12-07T09:10:37.6174386Z + System.Runtime.Extensions + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll + 2018-06-05T04:50:06Z + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll + 2019-12-07T09:10:36.086204Z + System.Linq + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll + 2021-02-09T23:13:40.5754059Z + System.Core + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll + 2019-12-07T09:10:36.086204Z + System.Globalization + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll + 2019-12-07T09:10:34.5552678Z + System.IO + + + + E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + 2018-06-05T04:53:44Z + Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll + 2020-09-04T22:37:08Z + System.Data + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll + 2019-12-07T09:10:34.5860246Z + System.Threading + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll + 2019-12-07T09:10:37.7113237Z + System.Reflection.Extensions + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll + 2019-12-07T09:10:34.5705678Z + System.Threading.Tasks + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll + 2020-06-05T05:04:05.335002Z + System.Configuration + + + + C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll + 2019-12-07T09:10:37.7737543Z + System.Xml + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.ExecutionCommon.dll + 2021-03-10T01:18:39.2588672Z + Microsoft.VisualStudio.QualityTools.ExecutionCommon + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Resource.dll + 2021-03-10T01:18:39.3078688Z + Microsoft.VisualStudio.QualityTools.Resource + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.WebTestFramework.dll + 2021-03-10T01:18:39.3588667Z + Microsoft.VisualStudio.QualityTools.WebTestFramework + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + 2021-03-10T01:18:39.3688664Z + Microsoft.VisualStudio.TestPlatform.ObjectModel + + + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg new file mode 100644 index 00000000..11b5cabf --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg new file mode 100644 index 00000000..d11cf041 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg new file mode 100644 index 00000000..f0148b3a --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg new file mode 100644 index 00000000..252166bb --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg new file mode 100644 index 00000000..3c30c365 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg new file mode 100644 index 00000000..79327232 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg new file mode 100644 index 00000000..c174eb5e --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg new file mode 100644 index 00000000..04b24ecc --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg new file mode 100644 index 00000000..567c11f3 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg new file mode 100644 index 00000000..bb225544 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg new file mode 100644 index 00000000..0e9a8601 --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm new file mode 100644 index 00000000..f2f23f7e --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm @@ -0,0 +1,71 @@ + + + + + + +Summary - Coverage Report + +
+

Summary

+ ++++ + + + + + + + + + + + + + + + +
Generated on:2021/3/26 - 下午 04:34:49
Parser:OpenCoverParser
Assemblies:3
Classes:3
Files:3
Covered lines:15
Uncovered lines:7
Coverable lines:22
Total lines:59
Line coverage:68.1% (15 of 22)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
+

Coverage History

+
+ +

Risk Hotspots

+ + +

No risk hotspots found.

+

Coverage

+ + +++++++++++ + + + + + + + + + +
NameCoveredUncoveredCoverableTotalLine coverageBranch coverage
ClassLibrary13691833.3%
  
 
ClassLibrary1.Calculation3691833.3%
  
 
UnitTestProject191102690%
  
 
UnitTestProject1.UnitTest191102690%
  
 
UnitTestProject230315100%
 
 
UnitTestProject2.UnitTest130315100%
 
 
+
+
+ + \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js new file mode 100644 index 00000000..160252eb --- /dev/null +++ b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js @@ -0,0 +1,274 @@ +/* Chartist.js 0.11.0 + * Copyright © 2017 Gion Kunz + * Free to use under either the WTFPL license or the MIT license. + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL + * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT + */ + +!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { + var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { + "use strict"; function d(a) { + var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { + var i = c.serialMap(b.normalized.series, function () { + return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) + }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") + } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) + } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) + }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a +}); + +var i, l, selectedLine = null; + +/* Navigate to hash without browser history entry */ +var navigateToHash = function () { + if (window.history !== undefined && window.history.replaceState !== undefined) { + window.history.replaceState(undefined, undefined, this.getAttribute("href")); + } +}; + +var hashLinks = document.getElementsByClassName('navigatetohash'); +for (i = 0, l = hashLinks.length; i < l; i++) { + hashLinks[i].addEventListener('click', navigateToHash); +} + +/* Switch test method */ +var switchTestMethod = function () { + var method = this.getAttribute("value"); + console.log("Selected test method: " + method); + + var lines, i, l, coverageData, lineAnalysis, cells; + + lines = document.querySelectorAll('.lineAnalysis tr'); + + for (i = 1, l = lines.length; i < l; i++) { + coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); + lineAnalysis = coverageData[method]; + cells = lines[i].querySelectorAll('td'); + if (lineAnalysis === undefined) { + lineAnalysis = coverageData.AllTestMethods; + if (lineAnalysis.LVS !== 'gray') { + cells[0].setAttribute('class', 'red'); + cells[1].innerText = cells[1].textContent = '0'; + cells[4].setAttribute('class', 'lightred'); + } + } else { + cells[0].setAttribute('class', lineAnalysis.LVS); + cells[1].innerText = cells[1].textContent = lineAnalysis.VC; + cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); + } + } +}; + +var testMethods = document.getElementsByClassName('switchtestmethod'); +for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].addEventListener('change', switchTestMethod); +} + +/* Highlight test method by line */ +var toggleLine = function () { + if (selectedLine === this) { + selectedLine = null; + } else { + selectedLine = null; + unhighlightTestMethods(); + highlightTestMethods.call(this); + selectedLine = this; + } + +}; +var highlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var lineAnalysis; + var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); + var testMethods = document.getElementsByClassName('testmethod'); + + for (i = 0, l = testMethods.length; i < l; i++) { + lineAnalysis = coverageData[testMethods[i].id]; + if (lineAnalysis === undefined) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } else { + testMethods[i].className += ' light' + lineAnalysis.LVS; + } + } +}; +var unhighlightTestMethods = function () { + if (selectedLine !== null) { + return; + } + + var testMethods = document.getElementsByClassName('testmethod'); + for (i = 0, l = testMethods.length; i < l; i++) { + testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); + } +}; +var coverableLines = document.getElementsByClassName('coverableline'); +for (i = 0, l = coverableLines.length; i < l; i++) { + coverableLines[i].addEventListener('click', toggleLine); + coverableLines[i].addEventListener('mouseenter', highlightTestMethods); + coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); +} + +/* History charts */ +var renderChart = function (chart) { + // Remove current children (e.g. PNG placeholder) + while (chart.firstChild) { + chart.firstChild.remove(); + } + + var chartData = window[chart.getAttribute('data-data')]; + var options = { + axisY: { + type: undefined, + onlyInteger: true + }, + lineSmooth: false, + low: 0, + high: 100, + scaleMinSpace: 20, + onlyInteger: true, + fullWidth: true + }; + var lineChart = new Chartist.Line(chart, { + labels: [], + series: chartData.series + }, options); + + /* Zoom */ + var zoomButtonDiv = document.createElement("div"); + zoomButtonDiv.className = "toggleZoom"; + var zoomButtonLink = document.createElement("a"); + zoomButtonLink.setAttribute("href", ""); + var zoomButtonText = document.createElement("i"); + zoomButtonText.className = "icon-search-plus"; + + zoomButtonLink.appendChild(zoomButtonText); + zoomButtonDiv.appendChild(zoomButtonLink); + + chart.appendChild(zoomButtonDiv); + + zoomButtonDiv.addEventListener('click', function (event) { + event.preventDefault(); + + if (options.axisY.type === undefined) { + options.axisY.type = Chartist.AutoScaleAxis; + zoomButtonText.className = "icon-search-minus"; + } else { + options.axisY.type = undefined; + zoomButtonText.className = "icon-search-plus"; + } + + lineChart.update(null, options); + }); + + var tooltip = document.createElement("div"); + tooltip.className = "tooltip"; + + chart.appendChild(tooltip); + + /* Tooltips */ + var showToolTip = function () { + var point = this; + var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); + + tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; + tooltip.style.display = 'block'; + }; + + var moveToolTip = function (event) { + var box = chart.getBoundingClientRect(); + var left = event.pageX - box.left - window.pageXOffset; + var top = event.pageY - box.top - window.pageYOffset; + + tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; + tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; + }; + + var hideToolTip = function () { + tooltip.style.display = 'none'; + }; + + chart.addEventListener('mousemove', moveToolTip); + + lineChart.on('created', function () { + var chartPoints = chart.getElementsByClassName('ct-point'); + for (i = 0, l = chartPoints.length; i < l; i++) { + chartPoints[i].addEventListener('mousemove', showToolTip); + chartPoints[i].addEventListener('mouseout', hideToolTip); + } + }); +}; + +var charts = document.getElementsByClassName('historychart'); +for (i = 0, l = charts.length; i < l; i++) { + renderChart(charts[i]); +} + +var assemblies = [ + { + "name": "ClassLibrary1", + "classes": [ + { "name": "ClassLibrary1.Calculation", "rp": "ClassLibrary1_Calculation.htm", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [33.3], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "lcq": 33.3, "cb": 0, "tb": 0, "bcq": 0 }] }, + ]}, + { + "name": "UnitTestProject1", + "classes": [ + { "name": "UnitTestProject1.UnitTest1", "rp": "UnitTestProject1_UnitTest1.htm", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [90], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "lcq": 90, "cb": 0, "tb": 0, "bcq": 0 }] }, + ]}, + { + "name": "UnitTestProject2", + "classes": [ + { "name": "UnitTestProject2.UnitTest1", "rp": "UnitTestProject2_UnitTest1.htm", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [100], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "lcq": 100, "cb": 0, "tb": 0, "bcq": 0 }] }, + ]}, +]; + +var historicCoverageExecutionTimes = []; + +var riskHotspotMetrics = [ +]; + +var riskHotspots = [ +]; + +var branchCoverageAvailable = true; + + +var translations = { +'top': 'Top:', +'all': 'All', +'assembly': 'Assembly', +'class': 'Class', +'method': 'Method', +'lineCoverage': 'LineCoverage', +'noGrouping': 'No grouping', +'byAssembly': 'By assembly', +'byNamespace': 'By namespace, Level:', +'all': 'All', +'collapseAll': 'Collapse all', +'expandAll': 'Expand all', +'grouping': 'Grouping:', +'filter': 'Filter:', +'name': 'Name', +'covered': 'Covered', +'uncovered': 'Uncovered', +'coverable': 'Coverable', +'total': 'Total', +'coverage': 'Line coverage', +'branchCoverage': 'Branch coverage', +'history': 'Coverage History', +'compareHistory': 'Compare with:', +'date': 'Date', +'allChanges': 'All changes', +'lineCoverageIncreaseOnly': 'Line coverage: Increase only', +'lineCoverageDecreaseOnly': 'Line coverage: Decrease only', +'branchCoverageIncreaseOnly': 'Branch coverage: Increase only', +'branchCoverageDecreaseOnly': 'Branch coverage: Decrease only' +}; + + +!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c",this._properties=n&&n.properties||{},this._zoneDelegate=new c(this,this._parent&&this._parent._zoneDelegate,n)}return n.assertZonePatched=function(){if(t.Promise!==T.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")},Object.defineProperty(n,"root",{get:function(){for(var t=n.current;t.parent;)t=t.parent;return t},enumerable:!0,configurable:!0}),Object.defineProperty(n,"current",{get:function(){return j.zone},enumerable:!0,configurable:!0}),Object.defineProperty(n,"currentTask",{get:function(){return M},enumerable:!0,configurable:!0}),n.__load_patch=function(o,i){if(T.hasOwnProperty(o))throw Error("Already loaded patch: "+o);if(!t["__Zone_disable_"+o]){var u="Zone:"+o;r(u),T[o]=i(t,n,O),e(u,u)}},Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"name",{get:function(){return this._name},enumerable:!0,configurable:!0}),n.prototype.get=function(t){var n=this.getZoneWith(t);if(n)return n._properties[t]},n.prototype.getZoneWith=function(t){for(var n=this;n;){if(n._properties.hasOwnProperty(t))return n;n=n._parent}return null},n.prototype.fork=function(t){if(!t)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,t)},n.prototype.wrap=function(t,n){if("function"!=typeof t)throw new Error("Expecting function got: "+t);var r=this._zoneDelegate.intercept(this,t,n),e=this;return function(){return e.runGuarded(r,this,arguments,n)}},n.prototype.run=function(t,n,r,e){void 0===n&&(n=void 0),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{return this._zoneDelegate.invoke(this,t,n,r,e)}finally{j=j.parent}},n.prototype.runGuarded=function(t,n,r,e){void 0===n&&(n=null),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{try{return this._zoneDelegate.invoke(this,t,n,r,e)}catch(o){if(this._zoneDelegate.handleError(this,o))throw o}}finally{j=j.parent}},n.prototype.runTask=function(t,n,r){if(t.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");if(t.state!==g||t.type!==F){var e=t.state!=_;e&&t._transitionTo(_,m),t.runCount++;var o=M;M=t,j={parent:j,zone:this};try{t.type==S&&t.data&&!t.data.isPeriodic&&(t.cancelFn=null);try{return this._zoneDelegate.invokeTask(this,t,n,r)}catch(i){if(this._zoneDelegate.handleError(this,i))throw i}}finally{t.state!==g&&t.state!==w&&(t.type==F||t.data&&t.data.isPeriodic?e&&t._transitionTo(m,_):(t.runCount=0,this._updateTaskCount(t,-1),e&&t._transitionTo(g,_,g))),j=j.parent,M=o}}},n.prototype.scheduleTask=function(t){if(t.zone&&t.zone!==this)for(var n=this;n;){if(n===t.zone)throw Error("can not reschedule task to "+this.name+" which is descendants of the original zone "+t.zone.name);n=n.parent}t._transitionTo(b,g);var r=[];t._zoneDelegates=r,t._zone=this;try{t=this._zoneDelegate.scheduleTask(this,t)}catch(e){throw t._transitionTo(w,b,g),this._zoneDelegate.handleError(this,e),e}return t._zoneDelegates===r&&this._updateTaskCount(t,1),t.state==b&&t._transitionTo(m,b),t},n.prototype.scheduleMicroTask=function(t,n,r,e){return this.scheduleTask(new a(x,t,n,r,e,null))},n.prototype.scheduleMacroTask=function(t,n,r,e,o){return this.scheduleTask(new a(S,t,n,r,e,o))},n.prototype.scheduleEventTask=function(t,n,r,e,o){return this.scheduleTask(new a(F,t,n,r,e,o))},n.prototype.cancelTask=function(t){if(t.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");t._transitionTo(k,m,_);try{this._zoneDelegate.cancelTask(this,t)}catch(n){throw t._transitionTo(w,k),this._zoneDelegate.handleError(this,n),n}return this._updateTaskCount(t,-1),t._transitionTo(g,k),t.runCount=0,t},n.prototype._updateTaskCount=function(t,n){var r=t._zoneDelegates;-1==n&&(t._zoneDelegates=null);for(var e=0;e0,macroTask:r.macroTask>0,eventTask:r.eventTask>0,change:t})},t}(),a=function(){function n(r,e,o,i,u,c){this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=r,this.source=e,this.data=i,this.scheduleFn=u,this.cancelFn=c,this.callback=o;var a=this;this.invoke=r===F&&i&&i.useG?n.invokeTask:function(){return n.invokeTask.call(t,a,this,arguments)}}return n.invokeTask=function(t,n,r){t||(t=this),K++;try{return t.runCount++,t.zone.runTask(t,n,r)}finally{1==K&&y(),K--}},Object.defineProperty(n.prototype,"zone",{get:function(){return this._zone},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"state",{get:function(){return this._state},enumerable:!0,configurable:!0}),n.prototype.cancelScheduleRequest=function(){this._transitionTo(g,b)},n.prototype._transitionTo=function(t,n,r){if(this._state!==n&&this._state!==r)throw new Error(this.type+" '"+this.source+"': can not transition to '"+t+"', expecting state '"+n+"'"+(r?" or '"+r+"'":"")+", was '"+this._state+"'.");this._state=t,t==g&&(this._zoneDelegates=null)},n.prototype.toString=function(){return this.data&&void 0!==this.data.handleId?this.data.handleId:Object.prototype.toString.call(this)},n.prototype.toJSON=function(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}},n}(),f=X("setTimeout"),s=X("Promise"),l=X("then"),h=[],p=!1;function v(n){0===K&&0===h.length&&(o||t[s]&&(o=t[s].resolve(0)),o?o[l](y):t[f](y,0)),n&&h.push(n)}function y(){if(!p){for(p=!0;h.length;){var t=h;h=[];for(var n=0;n=0;r--)"function"==typeof t[r]&&(t[r]=h(t[r],n+"_"+r));return t}function k(t){return!t||!1!==t.writable&&!("function"==typeof t.get&&void 0===t.set)}var w="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,x=!("nw"in g)&&void 0!==g.process&&"[object process]"==={}.toString.call(g.process),S=!x&&!w&&!(!y||!d.HTMLElement),F=void 0!==g.process&&"[object process]"==={}.toString.call(g.process)&&!w&&!(!y||!d.HTMLElement),T={},O=function(t){if(t=t||g.event){var n=T[t.type];n||(n=T[t.type]=v("ON_PROPERTY"+t.type));var r=(this||t.target||g)[n],e=r&&r.apply(this,arguments);return null==e||e||t.preventDefault(),e}};function j(r,e,o){var i=t(r,e);if(!i&&o&&t(o,e)&&(i={enumerable:!0,configurable:!0}),i&&i.configurable){delete i.writable,delete i.value;var u=i.get,c=i.set,a=e.substr(2),f=T[a];f||(f=T[a]=v("ON_PROPERTY"+a)),i.set=function(t){var n=this;n||r!==g||(n=g),n&&(n[f]&&n.removeEventListener(a,O),c&&c.apply(n,m),"function"==typeof t?(n[f]=t,n.addEventListener(a,O,!1)):n[f]=null)},i.get=function(){var t=this;if(t||r!==g||(t=g),!t)return null;var n=t[f];if(n)return n;if(u){var o=u&&u.call(this);if(o)return i.set.call(this,o),"function"==typeof t[b]&&t.removeAttribute(e),o}return null},n(r,e,i)}}function M(t,n,r){if(n)for(var e=0;e1?new c(n,r):new c(n),l=t(s,"onmessage");return l&&!1===l.configurable?(a=e(s),f=s,[i,u,"send","close"].forEach(function(t){a[t]=function(){var n=o.call(arguments);if(t===i||t===u){var r=n.length>0?n[0]:void 0;if(r){var e=Zone.__symbol__("ON_PROPERTY"+r);s[e]=a[e]}}return s[t].apply(s,n)}})):a=s,M(a,["close","error","message","open"],f),a};var a=r.WebSocket;for(var f in c)a[f]=c[f]}(0,a)}}var lt=v("unbound");Zone.__load_patch("util",function(t,n,r){r.patchOnProperties=M,r.patchMethod=X,r.bindArguments=_}),Zone.__load_patch("timers",function(t){G(t,"set","clear","Timeout"),G(t,"set","clear","Interval"),G(t,"set","clear","Immediate")}),Zone.__load_patch("requestAnimationFrame",function(t){G(t,"request","cancel","AnimationFrame"),G(t,"mozRequest","mozCancel","AnimationFrame"),G(t,"webkitRequest","webkitCancel","AnimationFrame")}),Zone.__load_patch("blocking",function(t,n){for(var r=["alert","prompt","confirm"],e=0;e=0&&"function"==typeof r[e.cbIdx]?p(e.name,r[e.cbIdx],e,i,null):t.apply(n,r)}})}()}),Zone.__load_patch("XHR",function(t,n){!function(n){var f=XMLHttpRequest.prototype,s=f[c],l=f[a];if(!s){var h=t.XMLHttpRequestEventTarget;if(h){var v=h.prototype;s=v[c],l=v[a]}}var y="readystatechange",d="scheduled";function g(t){XMLHttpRequest[i]=!1;var n=t.data,e=n.target,u=e[o];s||(s=e[c],l=e[a]),u&&l.call(e,y,u);var f=e[o]=function(){e.readyState===e.DONE&&!n.aborted&&XMLHttpRequest[i]&&t.state===d&&t.invoke()};return s.call(e,y,f),e[r]||(e[r]=t),k.apply(e,n.args),XMLHttpRequest[i]=!0,t}function b(){}function m(t){var n=t.data;return n.aborted=!0,w.apply(n.target,n.args)}var _=X(f,"open",function(){return function(t,n){return t[e]=0==n[2],t[u]=n[1],_.apply(t,n)}}),k=X(f,"send",function(){return function(t,n){return t[e]?k.apply(t,n):p("XMLHttpRequest.send",b,{target:t,url:t[u],isPeriodic:!1,delay:null,args:n,aborted:!1},g,m)}}),w=X(f,"abort",function(){return function(t){var n=t[r];if(n&&"string"==typeof n.type){if(null==n.cancelFn||n.data&&n.data.aborted)return;n.zone.cancelTask(n)}}})}();var r=v("xhrTask"),e=v("xhrSync"),o=v("xhrListener"),i=v("xhrScheduled"),u=v("xhrURL")}),Zone.__load_patch("geolocation",function(n){n.navigator&&n.navigator.geolocation&&function(n,r){for(var e=n.constructor.name,o=function(o){var i=r[o],u=n[i];if(u){if(!k(t(n,i)))return"continue";n[i]=function(t){var n=function(){return t.apply(this,_(arguments,e+"."+i))};return Z(n,t),n}(u)}},i=0;if;)a.call(t,u=c[f++])&&n.push(u);return n}},"1TsA":function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},"1sa7":function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},"25dN":function(t,n,r){var e=r("XKFU");e(e.S,"Object",{is:r("g6HL")})},"2OiF":function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"2Spj":function(t,n,r){var e=r("XKFU");e(e.P,"Function",{bind:r("8MEG")})},"2atp":function(t,n,r){var e=r("XKFU"),o=Math.atanh;e(e.S+e.F*!(o&&1/o(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},"3Lyj":function(t,n,r){var e=r("KroJ");t.exports=function(t,n,r){for(var o in n)e(t,o,n[o],r);return t}},"4A4+":function(t,n,r){r("2Spj"),r("f3/d"),r("IXt9"),t.exports=r("g3g5").Function},"4LiD":function(t,n,r){"use strict";var e=r("dyZX"),o=r("XKFU"),i=r("KroJ"),u=r("3Lyj"),c=r("Z6vF"),a=r("SlkY"),f=r("9gX7"),s=r("0/R4"),l=r("eeVq"),h=r("XMVh"),p=r("fyDq"),v=r("Xbzi");t.exports=function(t,n,r,y,d,g){var b=e[t],m=b,_=d?"set":"add",k=m&&m.prototype,w={},x=function(t){var n=k[t];i(k,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||k.forEach&&!l(function(){(new m).entries().next()}))){var S=new m,F=S[_](g?{}:-0,1)!=S,T=l(function(){S.has(1)}),O=h(function(t){new m(t)}),j=!g&&l(function(){for(var t=new m,n=5;n--;)t[_](n,n);return!t.has(-0)});O||((m=n(function(n,r){f(n,m,t);var e=v(new b,n,m);return null!=r&&a(r,d,e[_],e),e})).prototype=k,k.constructor=m),(T||j)&&(x("delete"),x("has"),d&&x("get")),(j||F)&&x(_),g&&k.clear&&delete k.clear}else m=y.getConstructor(n,t,d,_),u(m.prototype,r),c.NEED=!0;return p(m,t),w[t]=m,o(o.G+o.W+o.F*(m!=b),w),g||y.setStrong(m,t,d),m}},"4R4u":function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"5Pf0":function(t,n,r){var e=r("S/j/"),o=r("OP3Y");r("Xtr8")("getPrototypeOf",function(){return function(t){return o(e(t))}})},"69bn":function(t,n,r){var e=r("y3w9"),o=r("2OiF"),i=r("K0xU")("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||null==(r=e(u)[i])?n:o(r)}},"6AQ9":function(t,n,r){"use strict";var e=r("XKFU"),o=r("8a7r");e(e.S+e.F*r("eeVq")(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)o(r,t,arguments[t++]);return r.length=n,r}})},"6FMO":function(t,n,r){var e=r("0/R4"),o=r("EWmC"),i=r("K0xU")("species");t.exports=function(t){var n;return o(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!o(n.prototype)||(n=void 0),e(n)&&null===(n=n[i])&&(n=void 0)),void 0===n?Array:n}},"7h0T":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isNaN:function(t){return t!=t}})},"8+KV":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(0),i=r("LyE8")([].forEach,!0);e(e.P+e.F*!i,"Array",{forEach:function(t){return o(this,t,arguments[1])}})},"84bF":function(t,n,r){"use strict";r("OGtf")("small",function(t){return function(){return t(this,"small","","")}})},"8MEG":function(t,n,r){"use strict";var e=r("2OiF"),o=r("0/R4"),i=r("MfQN"),u=[].slice,c={};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),a=function(){var e=r.concat(u.call(arguments));return this instanceof a?function(t,n,r){if(!(n in c)){for(var e=[],o=0;o0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(o(this,"Map"),t);return n&&n.v},set:function(t,n){return e.def(o(this,"Map"),0===t?0:t,n)}},e,!0)},"9P93":function(t,n,r){var e=r("XKFU"),o=Math.imul;e(e.S+e.F*r("eeVq")(function(){return-5!=o(4294967295,5)||2!=o.length}),"Math",{imul:function(t,n){var r=+t,e=+n,o=65535&r,i=65535&e;return 0|o*i+((65535&r>>>16)*i+o*(65535&e>>>16)<<16>>>0)}})},"9VmF":function(t,n,r){"use strict";var e=r("XKFU"),o=r("ne8i"),i=r("0sh+"),u="".startsWith;e(e.P+e.F*r("UUeW")("startsWith"),"String",{startsWith:function(t){var n=i(this,t,"startsWith"),r=o(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return u?u.call(n,e,r):n.slice(r,r+e.length)===e}})},"9gX7":function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},A2zW:function(t,n,r){"use strict";var e=r("XKFU"),o=r("RYi7"),i=r("vvmO"),u=r("l0Rn"),c=1..toFixed,a=Math.floor,f=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l=function(t,n){for(var r=-1,e=n;++r<6;)f[r]=(e+=t*f[r])%1e7,e=a(e/1e7)},h=function(t){for(var n=6,r=0;--n>=0;)f[n]=a((r+=f[n])/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==f[t]){var r=String(f[t]);n=""===n?r:n+u.call("0",7-r.length)+r}return n},v=function(t,n,r){return 0===n?r:n%2==1?v(t,n-1,r*t):v(t*t,n/2,r)};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r("eeVq")(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,a=i(this,s),f=o(t),y="",d="0";if(f<0||f>20)throw RangeError(s);if(a!=a)return"NaN";if(a<=-1e21||a>=1e21)return String(a);if(a<0&&(y="-",a=-a),a>1e-21)if(r=(n=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n}(a*v(2,69,1))-69)<0?a*v(2,-n,1):a/v(2,n,1),r*=4503599627370496,(n=52-n)>0){for(l(0,r),e=f;e>=7;)l(1e7,0),e-=7;for(l(v(10,e,1),0),e=n-1;e>=23;)h(1<<23),e-=23;h(1<0?y+((c=d.length)<=f?"0."+u.call("0",f-c)+d:d.slice(0,c-f)+"."+d.slice(c-f)):y+d}})},A5AN:function(t,n,r){"use strict";var e=r("AvRE")(!0);t.exports=function(t,n,r){return n+(r?e(t,n).length:1)}},Afnz:function(t,n,r){"use strict";var e=r("LQAc"),o=r("XKFU"),i=r("KroJ"),u=r("Mukb"),c=r("hPIQ"),a=r("QaDb"),f=r("fyDq"),s=r("OP3Y"),l=r("K0xU")("iterator"),h=!([].keys&&"next"in[].keys()),p=function(){return this};t.exports=function(t,n,r,v,y,d,g){a(r,n,v);var b,m,_,k=function(t){if(!h&&t in F)return F[t];switch(t){case"keys":case"values":return function(){return new r(this,t)}}return function(){return new r(this,t)}},w=n+" Iterator",x="values"==y,S=!1,F=t.prototype,T=F[l]||F["@@iterator"]||y&&F[y],O=T||k(y),j=y?x?k("entries"):O:void 0,M="Array"==n&&F.entries||T;if(M&&(_=s(M.call(new t)))!==Object.prototype&&_.next&&(f(_,w,!0),e||"function"==typeof _[l]||u(_,l,p)),x&&T&&"values"!==T.name&&(S=!0,O=function(){return T.call(this)}),e&&!g||!h&&!S&&F[l]||u(F,l,O),c[n]=O,c[w]=p,y)if(b={values:x?O:k("values"),keys:d?O:k("keys"),entries:j},g)for(m in b)m in F||i(F,m,b[m]);else o(o.P+o.F*(h||S),n,b);return b}},AphP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("S/j/"),i=r("apmT");e(e.P+e.F*r("eeVq")(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=o(this),r=i(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},AvRE:function(t,n,r){var e=r("RYi7"),o=r("vhPU");t.exports=function(t){return function(n,r){var i,u,c=String(o(n)),a=e(r),f=c.length;return a<0||a>=f?t?"":void 0:(i=c.charCodeAt(a))<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536}}},BC7C:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{fround:r("kcoS")})},"BJ/l":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log1p:r("1sa7")})},BP8U:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.S+e.F*(Number.parseInt!=o),"Number",{parseInt:o})},Btvt:function(t,n,r){"use strict";var e=r("I8a+"),o={};o[r("K0xU")("toStringTag")]="z",o+""!="[object z]"&&r("KroJ")(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},"C/va":function(t,n,r){"use strict";var e=r("y3w9");t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},CkkT:function(t,n,r){var e=r("m0Pp"),o=r("Ymqv"),i=r("S/j/"),u=r("ne8i"),c=r("zRwo");t.exports=function(t,n){var r=1==t,a=2==t,f=3==t,s=4==t,l=6==t,h=5==t||l,p=n||c;return function(n,c,v){for(var y,d,g=i(n),b=o(g),m=e(c,v,3),_=u(b.length),k=0,w=r?p(n,_):a?p(n,0):void 0;_>k;k++)if((h||k in b)&&(d=m(y=b[k],k,g),t))if(r)w[k]=d;else if(d)switch(t){case 3:return!0;case 5:return y;case 6:return k;case 2:w.push(y)}else if(s)return!1;return l?-1:f||s?s:w}}},CuTL:function(t,n,r){r("fyVe"),r("U2t9"),r("2atp"),r("+auO"),r("MtdB"),r("Jcmo"),r("nzyx"),r("BC7C"),r("x8ZO"),r("9P93"),r("eHKK"),r("BJ/l"),r("pp/T"),r("CyHz"),r("bBoP"),r("x8Yj"),r("hLT2"),t.exports=r("g3g5").Math},CyHz:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{sign:r("lvtm")})},DNiP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduce,!0),"Array",{reduce:function(t){return o(this,t,arguments.length,arguments[1],!1)}})},DVgA:function(t,n,r){var e=r("zhAb"),o=r("4R4u");t.exports=Object.keys||function(t){return e(t,o)}},DW2E:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("freeze",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},EK0E:function(t,n,r){"use strict";var e,o=r("CkkT")(0),i=r("KroJ"),u=r("Z6vF"),c=r("czNK"),a=r("ZD67"),f=r("0/R4"),s=r("eeVq"),l=r("s5qY"),h=u.getWeak,p=Object.isExtensible,v=a.ufstore,y={},d=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},g={get:function(t){if(f(t)){var n=h(t);return!0===n?v(l(this,"WeakMap")).get(t):n?n[this._i]:void 0}},set:function(t,n){return a.def(l(this,"WeakMap"),t,n)}},b=t.exports=r("4LiD")("WeakMap",d,g,a,!0,!0);s(function(){return 7!=(new b).set((Object.freeze||Object)(y),7).get(y)})&&(c((e=a.getConstructor(d,"WeakMap")).prototype,g),u.NEED=!0,o(["delete","has","get","set"],function(t){var n=b.prototype,r=n[t];i(n,t,function(n,o){if(f(n)&&!p(n)){this._f||(this._f=new e);var i=this._f[t](n,o);return"set"==t?this:i}return r.call(this,n,o)})}))},EWmC:function(t,n,r){var e=r("LZWt");t.exports=Array.isArray||function(t){return"Array"==e(t)}},EemH:function(t,n,r){var e=r("UqcF"),o=r("RjD/"),i=r("aCFj"),u=r("apmT"),c=r("aagx"),a=r("xpql"),f=Object.getOwnPropertyDescriptor;n.f=r("nh4g")?f:function(t,n){if(t=i(t),n=u(n,!0),a)try{return f(t,n)}catch(r){}if(c(t,n))return o(!e.f.call(t,n),t[n])}},FEjr:function(t,n,r){"use strict";r("OGtf")("strike",function(t){return function(){return t(this,"strike","","")}})},FJW5:function(t,n,r){var e=r("hswa"),o=r("y3w9"),i=r("DVgA");t.exports=r("nh4g")?Object.defineProperties:function(t,n){o(t);for(var r,u=i(n),c=u.length,a=0;c>a;)e.f(t,r=u[a++],n[r]);return t}},FLlr:function(t,n,r){var e=r("XKFU");e(e.P,"String",{repeat:r("l0Rn")})},FlsD:function(t,n,r){var e=r("0/R4");r("Xtr8")("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},GNAe:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.G+e.F*(parseInt!=o),{parseInt:o})},H6hf:function(t,n,r){var e=r("y3w9");t.exports=function(t,n,r,o){try{return o?n(e(r)[0],r[1]):n(r)}catch(u){var i=t.return;throw void 0!==i&&e(i.call(t)),u}}},"HAE/":function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperty:r("hswa").f})},HEwt:function(t,n,r){"use strict";var e=r("m0Pp"),o=r("XKFU"),i=r("S/j/"),u=r("H6hf"),c=r("M6Qj"),a=r("ne8i"),f=r("8a7r"),s=r("J+6e");o(o.S+o.F*!r("XMVh")(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,o,l,h=i(t),p="function"==typeof this?this:Array,v=arguments.length,y=v>1?arguments[1]:void 0,d=void 0!==y,g=0,b=s(h);if(d&&(y=e(y,v>2?arguments[2]:void 0,2)),null==b||p==Array&&c(b))for(r=new p(n=a(h.length));n>g;g++)f(r,g,d?y(h[g],g):h[g]);else for(l=b.call(h),r=new p;!(o=l.next()).done;g++)f(r,g,d?u(l,y,[o.value,g],!0):o.value);return r.length=g,r}})},I78e:function(t,n,r){"use strict";var e=r("XKFU"),o=r("+rLv"),i=r("LZWt"),u=r("d/Gc"),c=r("ne8i"),a=[].slice;e(e.P+e.F*r("eeVq")(function(){o&&a.call(o)}),"Array",{slice:function(t,n){var r=c(this.length),e=i(this);if(n=void 0===n?r:n,"Array"==e)return a.call(this,t,n);for(var o=u(t,r),f=u(n,r),s=c(f-o),l=new Array(s),h=0;h1?arguments[1]:void 0)}}),r("nGyu")(i)},"IU+Z":function(t,n,r){"use strict";r("sMXx");var e=r("KroJ"),o=r("Mukb"),i=r("eeVq"),u=r("vhPU"),c=r("K0xU"),a=r("Ugos"),f=c("species"),s=!i(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")}),l=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var r="ab".split(t);return 2===r.length&&"a"===r[0]&&"b"===r[1]}();t.exports=function(t,n,r){var h=c(t),p=!i(function(){var n={};return n[h]=function(){return 7},7!=""[t](n)}),v=p?!i(function(){var n=!1,r=/a/;return r.exec=function(){return n=!0,null},"split"===t&&(r.constructor={},r.constructor[f]=function(){return r}),r[h](""),!n}):void 0;if(!p||!v||"replace"===t&&!s||"split"===t&&!l){var y=/./[h],d=r(u,h,""[t],function(t,n,r,e,o){return n.exec===a?p&&!o?{done:!0,value:y.call(n,r,e)}:{done:!0,value:t.call(r,n,e)}:{done:!1}}),g=d[1];e(String.prototype,t,d[0]),o(RegExp.prototype,h,2==n?function(t,n){return g.call(t,this,n)}:function(t){return g.call(t,this)})}}},IXt9:function(t,n,r){"use strict";var e=r("0/R4"),o=r("OP3Y"),i=r("K0xU")("hasInstance"),u=Function.prototype;i in u||r("hswa").f(u,i,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=o(t);)if(this.prototype===t)return!0;return!1}})},Iw71:function(t,n,r){var e=r("0/R4"),o=r("dyZX").document,i=e(o)&&e(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"J+6e":function(t,n,r){var e=r("I8a+"),o=r("K0xU")("iterator"),i=r("hPIQ");t.exports=r("g3g5").getIteratorMethod=function(t){if(null!=t)return t[o]||t["@@iterator"]||i[e(t)]}},JCqj:function(t,n,r){"use strict";r("OGtf")("sup",function(t){return function(){return t(this,"sup","","")}})},Jcmo:function(t,n,r){var e=r("XKFU"),o=Math.exp;e(e.S,"Math",{cosh:function(t){return(o(t=+t)+o(-t))/2}})},JduL:function(t,n,r){r("Xtr8")("getOwnPropertyNames",function(){return r("e7yV").f})},JiEa:function(t,n){n.f=Object.getOwnPropertySymbols},K0xU:function(t,n,r){var e=r("VTer")("wks"),o=r("ylqs"),i=r("dyZX").Symbol,u="function"==typeof i;(t.exports=function(t){return e[t]||(e[t]=u&&i[t]||(u?i:o)("Symbol."+t))}).store=e},KKXr:function(t,n,r){"use strict";var e=r("quPj"),o=r("y3w9"),i=r("69bn"),u=r("A5AN"),c=r("ne8i"),a=r("Xxuz"),f=r("Ugos"),s=Math.min,l=[].push,h=!!function(){try{return new RegExp("x","y")}catch(t){}}();r("IU+Z")("split",2,function(t,n,r,p){var v;return v="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var o=String(this);if(void 0===t&&0===n)return[];if(!e(t))return r.call(o,t,n);for(var i,u,c,a=[],s=0,h=void 0===n?4294967295:n>>>0,p=new RegExp(t.source,(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":"")+"g");(i=f.call(p,o))&&!((u=p.lastIndex)>s&&(a.push(o.slice(s,i.index)),i.length>1&&i.index=h));)p.lastIndex===i.index&&p.lastIndex++;return s===o.length?!c&&p.test("")||a.push(""):a.push(o.slice(s)),a.length>h?a.slice(0,h):a}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:r.call(this,t,n)}:r,[function(r,e){var o=t(this),i=null==r?void 0:r[n];return void 0!==i?i.call(r,o,e):v.call(String(o),r,e)},function(t,n){var e=p(v,t,this,n,v!==r);if(e.done)return e.value;var f=o(t),l=String(this),y=i(f,RegExp),d=f.unicode,g=new y(h?f:"^(?:"+f.source+")",(f.ignoreCase?"i":"")+(f.multiline?"m":"")+(f.unicode?"u":"")+(h?"y":"g")),b=void 0===n?4294967295:n>>>0;if(0===b)return[];if(0===l.length)return null===a(g,l)?[l]:[];for(var m=0,_=0,k=[];_document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a.prototype[i[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c.prototype=e(t),r=new c,c.prototype=null,r[u]=t):r=a(),void 0===n?r:o(r,n)}},L9s1:function(t,n,r){"use strict";var e=r("XKFU"),o=r("0sh+");e(e.P+e.F*r("UUeW")("includes"),"String",{includes:function(t){return!!~o(this,t,"includes").indexOf(t,arguments.length>1?arguments[1]:void 0)}})},LK8F:function(t,n,r){var e=r("XKFU");e(e.S,"Array",{isArray:r("EWmC")})},LQAc:function(t,n){t.exports=!1},LVwc:function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},LZWt:function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},Ljet:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},Lmuc:function(t,n,r){r("xfY5"),r("A2zW"),r("VKir"),r("Ljet"),r("/KAi"),r("fN96"),r("7h0T"),r("sbF8"),r("h/M4"),r("knhD"),r("XfKG"),r("BP8U"),t.exports=r("g3g5").Number},LyE8:function(t,n,r){"use strict";var e=r("eeVq");t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},M6Qj:function(t,n,r){var e=r("hPIQ"),o=r("K0xU")("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||i[o]===t)}},MfQN:function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},MtdB:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},Mukb:function(t,n,r){var e=r("hswa"),o=r("RjD/");t.exports=r("nh4g")?function(t,n,r){return e.f(t,n,o(1,r))}:function(t,n,r){return t[n]=r,t}},N8g3:function(t,n,r){n.f=r("K0xU")},Nr18:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=function(t){for(var n=e(this),r=i(n.length),u=arguments.length,c=o(u>1?arguments[1]:void 0,r),a=u>2?arguments[2]:void 0,f=void 0===a?r:o(a,r);f>c;)n[c++]=t;return n}},Nz9U:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=[].join;e(e.P+e.F*(r("Ymqv")!=Object||!r("LyE8")(i)),"Array",{join:function(t){return i.call(o(this),void 0===t?",":t)}})},OEbY:function(t,n,r){r("nh4g")&&"g"!=/./g.flags&&r("hswa").f(RegExp.prototype,"flags",{configurable:!0,get:r("C/va")})},OG14:function(t,n,r){"use strict";var e=r("y3w9"),o=r("g6HL"),i=r("Xxuz");r("IU+Z")("search",1,function(t,n,r,u){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=u(r,t,this);if(n.done)return n.value;var c=e(t),a=String(this),f=c.lastIndex;o(f,0)||(c.lastIndex=0);var s=i(c,a);return o(c.lastIndex,f)||(c.lastIndex=f),null===s?-1:s.index}]})},OGtf:function(t,n,r){var e=r("XKFU"),o=r("eeVq"),i=r("vhPU"),u=/"/g,c=function(t,n,r,e){var o=String(i(t)),c="<"+n;return""!==r&&(c+=" "+r+'="'+String(e).replace(u,""")+'"'),c+">"+o+""};t.exports=function(t,n){var r={};r[t]=n(c),e(e.P+e.F*o(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},OP3Y:function(t,n,r){var e=r("aagx"),o=r("S/j/"),i=r("YTvA")("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=o(t),e(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},OnI7:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("LQAc"),u=r("N8g3"),c=r("hswa").f;t.exports=function(t){var n=o.Symbol||(o.Symbol=i?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},Oyvg:function(t,n,r){var e=r("dyZX"),o=r("Xbzi"),i=r("hswa").f,u=r("kJMx").f,c=r("quPj"),a=r("C/va"),f=e.RegExp,s=f,l=f.prototype,h=/a/g,p=/a/g,v=new f(h)!==h;if(r("nh4g")&&(!v||r("eeVq")(function(){return p[r("K0xU")("match")]=!1,f(h)!=h||f(p)==p||"/a/i"!=f(h,"i")}))){f=function(t,n){var r=this instanceof f,e=c(t),i=void 0===n;return!r&&e&&t.constructor===f&&i?t:o(v?new s(e&&!i?t.source:t,n):s((e=t instanceof f)?t.source:t,e&&i?a.call(t):n),r?this:l,f)};for(var y=function(t){t in f||i(f,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})},d=u(s),g=0;d.length>g;)y(d[g++]);l.constructor=f,f.prototype=l,r("KroJ")(e,"RegExp",f)}r("elZq")("RegExp")},PKUr:function(t,n,r){var e=r("dyZX").parseInt,o=r("qncB").trim,i=r("/e88"),u=/^[-+]?0[xX]/;t.exports=8!==e(i+"08")||22!==e(i+"0x16")?function(t,n){var r=o(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},QaDb:function(t,n,r){"use strict";var e=r("Kuth"),o=r("RjD/"),i=r("fyDq"),u={};r("Mukb")(u,r("K0xU")("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:o(1,r)}),i(t,n+" Iterator")}},RW0V:function(t,n,r){var e=r("S/j/"),o=r("DVgA");r("Xtr8")("keys",function(){return function(t){return o(e(t))}})},RYi7:function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},"RjD/":function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},"S/j/":function(t,n,r){var e=r("vhPU");t.exports=function(t){return Object(e(t))}},SMB2:function(t,n,r){"use strict";r("OGtf")("bold",function(t){return function(){return t(this,"b","","")}})},SPin:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduceRight,!0),"Array",{reduceRight:function(t){return o(this,t,arguments.length,arguments[1],!0)}})},SRfc:function(t,n,r){"use strict";var e=r("y3w9"),o=r("ne8i"),i=r("A5AN"),u=r("Xxuz");r("IU+Z")("match",1,function(t,n,r,c){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=c(r,t,this);if(n.done)return n.value;var a=e(t),f=String(this);if(!a.global)return u(a,f);var s=a.unicode;a.lastIndex=0;for(var l,h=[],p=0;null!==(l=u(a,f));){var v=String(l[0]);h[p]=v,""===v&&(a.lastIndex=i(f,o(a.lastIndex),s)),p++}return 0===p?null:h}]})},SlkY:function(t,n,r){var e=r("m0Pp"),o=r("H6hf"),i=r("M6Qj"),u=r("y3w9"),c=r("ne8i"),a=r("J+6e"),f={},s={};(n=t.exports=function(t,n,r,l,h){var p,v,y,d,g=h?function(){return t}:a(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(i(g)){for(p=c(t.length);p>m;m++)if((d=n?b(u(v=t[m])[0],v[1]):b(t[m]))===f||d===s)return d}else for(y=g.call(t);!(v=y.next()).done;)if((d=o(y,b,v.value,n))===f||d===s)return d}).BREAK=f,n.RETURN=s},T39b:function(t,n,r){"use strict";var e=r("wmvG"),o=r("s5qY");t.exports=r("4LiD")("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(o(this,"Set"),t=0===t?0:t,t)}},e)},Tze0:function(t,n,r){"use strict";r("qncB")("trim",function(t){return function(){return t(this,3)}})},U2t9:function(t,n,r){var e=r("XKFU"),o=Math.asinh;e(e.S+e.F*!(o&&1/o(0)>0),"Math",{asinh:function t(n){return isFinite(n=+n)&&0!=n?n<0?-t(-n):Math.log(n+Math.sqrt(n*n+1)):n}})},UUeW:function(t,n,r){var e=r("K0xU")("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(o){}}return!0}},Ugos:function(t,n,r){"use strict";var e,o,i=r("C/va"),u=RegExp.prototype.exec,c=String.prototype.replace,a=u,f=(o=/b*/g,u.call(e=/a/,"a"),u.call(o,"a"),0!==e.lastIndex||0!==o.lastIndex),s=void 0!==/()??/.exec("")[1];(f||s)&&(a=function(t){var n,r,e,o,a=this;return s&&(r=new RegExp("^"+a.source+"$(?!\\s)",i.call(a))),f&&(n=a.lastIndex),e=u.call(a,t),f&&e&&(a.lastIndex=a.global?e.index+e[0].length:n),s&&e&&e.length>1&&c.call(e[0],r,function(){for(o=1;ou;){if(n=+arguments[u++],o(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?i(n):i(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},WLL4:function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperties:r("FJW5")})},XKFU:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("Mukb"),u=r("KroJ"),c=r("m0Pp"),a=function(t,n,r){var f,s,l,h,p=t&a.F,v=t&a.G,y=t&a.P,d=t&a.B,g=v?e:t&a.S?e[n]||(e[n]={}):(e[n]||{}).prototype,b=v?o:o[n]||(o[n]={}),m=b.prototype||(b.prototype={});for(f in v&&(r=n),r)l=((s=!p&&g&&void 0!==g[f])?g:r)[f],h=d&&s?c(l,e):y&&"function"==typeof l?c(Function.call,l):l,g&&u(g,f,l,t&a.U),b[f]!=l&&i(b,f,h),y&&m[f]!=l&&(m[f]=l)};e.core=o,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},XMVh:function(t,n,r){var e=r("K0xU")("iterator"),o=!1;try{var i=[7][e]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(u){}t.exports=function(t,n){if(!n&&!o)return!1;var r=!1;try{var i=[7],c=i[e]();c.next=function(){return{done:r=!0}},i[e]=function(){return c},t(i)}catch(u){}return r}},Xbzi:function(t,n,r){var e=r("0/R4"),o=r("i5dc").set;t.exports=function(t,n,r){var i,u=n.constructor;return u!==r&&"function"==typeof u&&(i=u.prototype)!==r.prototype&&e(i)&&o&&o(t,i),t}},XfKG:function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.S+e.F*(Number.parseFloat!=o),"Number",{parseFloat:o})},XfO3:function(t,n,r){"use strict";var e=r("AvRE")(!0);r("Afnz")(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},Xtr8:function(t,n,r){var e=r("XKFU"),o=r("g3g5"),i=r("eeVq");t.exports=function(t,n){var r=(o.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*i(function(){r(1)}),"Object",u)}},Xxuz:function(t,n,r){"use strict";var e=r("I8a+"),o=RegExp.prototype.exec;t.exports=function(t,n){var r=t.exec;if("function"==typeof r){var i=r.call(t,n);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==e(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},YJVH:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(4);e(e.P+e.F*!r("LyE8")([].every,!0),"Array",{every:function(t){return o(this,t,arguments[1])}})},YTvA:function(t,n,r){var e=r("VTer")("keys"),o=r("ylqs");t.exports=function(t){return e[t]||(e[t]=o(t))}},Ymqv:function(t,n,r){var e=r("LZWt");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},Z6vF:function(t,n,r){var e=r("ylqs")("meta"),o=r("0/R4"),i=r("aagx"),u=r("hswa").f,c=0,a=Object.isExtensible||function(){return!0},f=!r("eeVq")(function(){return a(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=t.exports={KEY:e,NEED:!1,fastKey:function(t,n){if(!o(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,e)){if(!a(t))return"F";if(!n)return"E";s(t)}return t[e].i},getWeak:function(t,n){if(!i(t,e)){if(!a(t))return!0;if(!n)return!1;s(t)}return t[e].w},onFreeze:function(t){return f&&l.NEED&&a(t)&&!i(t,e)&&s(t),t}}},ZD67:function(t,n,r){"use strict";var e=r("3Lyj"),o=r("Z6vF").getWeak,i=r("y3w9"),u=r("0/R4"),c=r("9gX7"),a=r("SlkY"),f=r("CkkT"),s=r("aagx"),l=r("s5qY"),h=f(5),p=f(6),v=0,y=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},g=function(t,n){return h(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=g(this,t);if(n)return n[1]},has:function(t){return!!g(this,t)},set:function(t,n){var r=g(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=p(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,i){var f=t(function(t,e){c(t,f,n,"_i"),t._t=n,t._i=v++,t._l=void 0,null!=e&&a(e,r,t[i],t)});return e(f.prototype,{delete:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).delete(t):r&&s(r,this._i)&&delete r[this._i]},has:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).has(t):r&&s(r,this._i)}}),f},def:function(t,n,r){var e=o(i(n),!0);return!0===e?y(t).set(n,r):e[t._i]=r,t},ufstore:y}},Zshi:function(t,n,r){var e=r("0/R4");r("Xtr8")("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},Zz4T:function(t,n,r){"use strict";r("OGtf")("sub",function(t){return function(){return t(this,"sub","","")}})},a1Th:function(t,n,r){"use strict";r("OEbY");var e=r("y3w9"),o=r("C/va"),i=r("nh4g"),u=/./.toString,c=function(t){r("KroJ")(RegExp.prototype,"toString",t,!0)};r("eeVq")(function(){return"/a/b"!=u.call({source:"a",flags:"b"})})?c(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!i&&t instanceof RegExp?o.call(t):void 0)}):"toString"!=u.name&&c(function(){return u.call(this)})},aCFj:function(t,n,r){var e=r("Ymqv"),o=r("vhPU");t.exports=function(t){return e(o(t))}},aagx:function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},apmT:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t))return t;var r,o;if(n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;if("function"==typeof(r=t.valueOf)&&!e(o=r.call(t)))return o;if(!n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},bBoP:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S+e.F*r("eeVq")(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(o(t)-o(-t))/2:(i(t-1)-i(-t-1))*(Math.E/2)}})},bDcW:function(t,n,r){"use strict";r("OGtf")("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},bHtr:function(t,n,r){var e=r("XKFU");e(e.P,"Array",{fill:r("Nr18")}),r("nGyu")("fill")},bWfx:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(1);e(e.P+e.F*!r("LyE8")([].map,!0),"Array",{map:function(t){return o(this,t,arguments[1])}})},czNK:function(t,n,r){"use strict";var e=r("DVgA"),o=r("JiEa"),i=r("UqcF"),u=r("S/j/"),c=r("Ymqv"),a=Object.assign;t.exports=!a||r("eeVq")(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=a({},t)[r]||Object.keys(a({},n)).join("")!=e})?function(t,n){for(var r=u(t),a=arguments.length,f=1,s=o.f,l=i.f;a>f;)for(var h,p=c(arguments[f++]),v=s?e(p).concat(s(p)):e(p),y=v.length,d=0;y>d;)l.call(p,h=v[d++])&&(r[h]=p[h]);return r}:a},"d/Gc":function(t,n,r){var e=r("RYi7"),o=Math.max,i=Math.min;t.exports=function(t,n){return(t=e(t))<0?o(t+n,0):i(t,n)}},"dE+T":function(t,n,r){var e=r("XKFU");e(e.P,"Array",{copyWithin:r("upKx")}),r("nGyu")("copyWithin")},dQfE:function(t,n,r){r("XfO3"),r("LK8F"),r("HEwt"),r("6AQ9"),r("Nz9U"),r("I78e"),r("Vd3H"),r("8+KV"),r("bWfx"),r("0l/t"),r("dZ+Y"),r("YJVH"),r("DNiP"),r("SPin"),r("V+eJ"),r("mGWK"),r("dE+T"),r("bHtr"),r("dRSK"),r("INYr"),r("0E+W"),r("yt8O"),t.exports=r("g3g5").Array},dRSK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(5),i=!0;"find"in[]&&Array(1).find(function(){i=!1}),e(e.P+e.F*i,"Array",{find:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}}),r("nGyu")("find")},"dZ+Y":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(3);e(e.P+e.F*!r("LyE8")([].some,!0),"Array",{some:function(t){return o(this,t,arguments[1])}})},dyZX:function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},e7yV:function(t,n,r){var e=r("aCFj"),o=r("kJMx").f,i={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return u&&"[object Window]"==i.call(t)?function(t){try{return o(t)}catch(n){return u.slice()}}(t):o(e(t))}},eHKK:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log10:function(t){return Math.log(t)*Math.LOG10E}})},eI33:function(t,n,r){var e=r("XKFU"),o=r("aCFj"),i=r("ne8i");e(e.S,"String",{raw:function(t){for(var n=o(t.raw),r=i(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c=0:l>h;h+=p)h in s&&(c=n(c,s[h],h,f));return c}},"f3/d":function(t,n,r){var e=r("hswa").f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||r("nh4g")&&e(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},fN96:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isInteger:r("nBIS")})},fyDq:function(t,n,r){var e=r("hswa").f,o=r("aagx"),i=r("K0xU")("toStringTag");t.exports=function(t,n,r){t&&!o(t=r?t:t.prototype,i)&&e(t,i,{configurable:!0,value:n})}},fyVe:function(t,n,r){var e=r("XKFU"),o=r("1sa7"),i=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:o(t-1+i(t-1)*i(t+1))}})},g3g5:function(t,n){var r=t.exports={version:"2.6.1"};"number"==typeof __e&&(__e=r)},g4EE:function(t,n,r){"use strict";var e=r("y3w9"),o=r("apmT");t.exports=function(t){if("string"!==t&&"number"!==t&&"default"!==t)throw TypeError("Incorrect hint");return o(e(this),"number"!=t)}},g6HL:function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},"h/M4":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},h7Nl:function(t,n,r){var e=Date.prototype,o=e.toString,i=e.getTime;new Date(NaN)+""!="Invalid Date"&&r("KroJ")(e,"toString",function(){var t=i.call(this);return t==t?o.call(this):"Invalid Date"})},hEkN:function(t,n,r){"use strict";r("OGtf")("anchor",function(t){return function(n){return t(this,"a","name",n)}})},hHhE:function(t,n,r){var e=r("XKFU");e(e.S,"Object",{create:r("Kuth")})},hLT2:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},"hN/g":function(t,n,r){"use strict";r.r(n),r("dQfE"),r("nx1v"),r("4A4+"),r("qKs0"),r("CuTL"),r("Lmuc"),r("99sg"),r("ifmr"),r("oka+"),r("rfyP"),r("VXxg"),r("V5/Y"),r("vqGA"),r("hYbK"),r("0TWp")},hPIQ:function(t,n){t.exports={}},hYbK:function(t,n,r){r("Btvt"),r("yt8O"),r("EK0E"),t.exports=r("g3g5").WeakMap},hswa:function(t,n,r){var e=r("y3w9"),o=r("xpql"),i=r("apmT"),u=Object.defineProperty;n.f=r("nh4g")?Object.defineProperty:function(t,n,r){if(e(t),n=i(n,!0),e(r),o)try{return u(t,n,r)}catch(c){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},i5dc:function(t,n,r){var e=r("0/R4"),o=r("y3w9"),i=function(t,n){if(o(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{(e=r("m0Pp")(Function.call,r("EemH").f(Object.prototype,"__proto__").set,2))(t,[]),n=!(t instanceof Array)}catch(o){n=!0}return function(t,r){return i(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:i}},ifmr:function(t,n,r){r("tyy+"),t.exports=r("g3g5").parseFloat},ioFf:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("nh4g"),u=r("XKFU"),c=r("KroJ"),a=r("Z6vF").KEY,f=r("eeVq"),s=r("VTer"),l=r("fyDq"),h=r("ylqs"),p=r("K0xU"),v=r("N8g3"),y=r("OnI7"),d=r("1MBn"),g=r("EWmC"),b=r("y3w9"),m=r("0/R4"),_=r("aCFj"),k=r("apmT"),w=r("RjD/"),x=r("Kuth"),S=r("e7yV"),F=r("EemH"),T=r("hswa"),O=r("DVgA"),j=F.f,M=T.f,K=S.f,U=e.Symbol,X=e.JSON,Z=X&&X.stringify,z=p("_hidden"),E=p("toPrimitive"),D={}.propertyIsEnumerable,A=s("symbol-registry"),L=s("symbols"),I=s("op-symbols"),P=Object.prototype,C="function"==typeof U,q=e.QObject,V=!q||!q.prototype||!q.prototype.findChild,R=i&&f(function(){return 7!=x(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=j(P,n);e&&delete P[n],M(t,n,r),e&&t!==P&&M(P,n,e)}:M,G=function(t){var n=L[t]=x(U.prototype);return n._k=t,n},J=C&&"symbol"==typeof U.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof U},Y=function(t,n,r){return t===P&&Y(I,n,r),b(t),n=k(n,!0),b(r),o(L,n)?(r.enumerable?(o(t,z)&&t[z][n]&&(t[z][n]=!1),r=x(r,{enumerable:w(0,!1)})):(o(t,z)||M(t,z,w(1,{})),t[z][n]=!0),R(t,n,r)):M(t,n,r)},H=function(t,n){b(t);for(var r,e=d(n=_(n)),o=0,i=e.length;i>o;)Y(t,r=e[o++],n[r]);return t},N=function(t){var n=D.call(this,t=k(t,!0));return!(this===P&&o(L,t)&&!o(I,t))&&(!(n||!o(this,t)||!o(L,t)||o(this,z)&&this[z][t])||n)},W=function(t,n){if(t=_(t),n=k(n,!0),t!==P||!o(L,n)||o(I,n)){var r=j(t,n);return!r||!o(L,n)||o(t,z)&&t[z][n]||(r.enumerable=!0),r}},B=function(t){for(var n,r=K(_(t)),e=[],i=0;r.length>i;)o(L,n=r[i++])||n==z||n==a||e.push(n);return e},Q=function(t){for(var n,r=t===P,e=K(r?I:_(t)),i=[],u=0;e.length>u;)!o(L,n=e[u++])||r&&!o(P,n)||i.push(L[n]);return i};C||(c((U=function(){if(this instanceof U)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===P&&n.call(I,r),o(this,z)&&o(this[z],t)&&(this[z][t]=!1),R(this,t,w(1,r))};return i&&V&&R(P,t,{configurable:!0,set:n}),G(t)}).prototype,"toString",function(){return this._k}),F.f=W,T.f=Y,r("kJMx").f=S.f=B,r("UqcF").f=N,r("JiEa").f=Q,i&&!r("LQAc")&&c(P,"propertyIsEnumerable",N,!0),v.f=function(t){return G(p(t))}),u(u.G+u.W+u.F*!C,{Symbol:U});for(var $="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),tt=0;$.length>tt;)p($[tt++]);for(var nt=O(p.store),rt=0;nt.length>rt;)y(nt[rt++]);u(u.S+u.F*!C,"Symbol",{for:function(t){return o(A,t+="")?A[t]:A[t]=U(t)},keyFor:function(t){if(!J(t))throw TypeError(t+" is not a symbol!");for(var n in A)if(A[n]===t)return n},useSetter:function(){V=!0},useSimple:function(){V=!1}}),u(u.S+u.F*!C,"Object",{create:function(t,n){return void 0===n?x(t):H(x(t),n)},defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:W,getOwnPropertyNames:B,getOwnPropertySymbols:Q}),X&&u(u.S+u.F*(!C||f(function(){var t=U();return"[null]"!=Z([t])||"{}"!=Z({a:t})||"{}"!=Z(Object(t))})),"JSON",{stringify:function(t){for(var n,r,e=[t],o=1;arguments.length>o;)e.push(arguments[o++]);if(r=n=e[1],(m(n)||void 0!==t)&&!J(t))return g(n)||(n=function(t,n){if("function"==typeof r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,Z.apply(X,e)}}),U.prototype[E]||r("Mukb")(U.prototype,E,U.prototype.valueOf),l(U,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},jqX0:function(t,n,r){var e=r("XKFU"),o=r("jtBr");e(e.P+e.F*(Date.prototype.toISOString!==o),"Date",{toISOString:o})},jtBr:function(t,n,r){"use strict";var e=r("eeVq"),o=Date.prototype.getTime,i=Date.prototype.toISOString,u=function(t){return t>9?t:"0"+t};t.exports=e(function(){return"0385-07-25T07:06:39.999Z"!=i.call(new Date(-5e13-1))})||!e(function(){i.call(new Date(NaN))})?function(){if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}:i},kJMx:function(t,n,r){var e=r("zhAb"),o=r("4R4u").concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,o)}},kcoS:function(t,n,r){var e=r("lvtm"),o=Math.pow,i=o(2,-52),u=o(2,-23),c=o(2,127)*(2-u),a=o(2,-126);t.exports=Math.fround||function(t){var n,r,o=Math.abs(t),f=e(t);return oc||r!=r?f*(1/0):f*r}},knhD:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},l0Rn:function(t,n,r){"use strict";var e=r("RYi7"),o=r("vhPU");t.exports=function(t){var n=String(o(this)),r="",i=e(t);if(i<0||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(n+=n))1&i&&(r+=n);return r}},lvtm:function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},m0Pp:function(t,n,r){var e=r("2OiF");t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,o){return t.call(n,r,e,o)}}return function(){return t.apply(n,arguments)}}},mGWK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=r("RYi7"),u=r("ne8i"),c=[].lastIndexOf,a=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(a||!r("LyE8")(c)),"Array",{lastIndexOf:function(t){if(a)return c.apply(this,arguments)||0;var n=o(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,i(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},mYba:function(t,n,r){var e=r("aCFj"),o=r("EemH").f;r("Xtr8")("getOwnPropertyDescriptor",function(){return function(t,n){return o(e(t),n)}})},mura:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("preventExtensions",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},nBIS:function(t,n,r){var e=r("0/R4"),o=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&o(t)===t}},nGyu:function(t,n,r){var e=r("K0xU")("unscopables"),o=Array.prototype;null==o[e]&&r("Mukb")(o,e,{}),t.exports=function(t){o[e][t]=!0}},nIY7:function(t,n,r){"use strict";r("OGtf")("big",function(t){return function(){return t(this,"big","","")}})},ne8i:function(t,n,r){var e=r("RYi7"),o=Math.min;t.exports=function(t){return t>0?o(e(t),9007199254740991):0}},nh4g:function(t,n,r){t.exports=!r("eeVq")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},nsiH:function(t,n,r){"use strict";r("OGtf")("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},nx1v:function(t,n,r){r("eM6i"),r("AphP"),r("jqX0"),r("h7Nl"),r("yM4b"),t.exports=Date},nzyx:function(t,n,r){var e=r("XKFU"),o=r("LVwc");e(e.S+e.F*(o!=Math.expm1),"Math",{expm1:o})},oDIu:function(t,n,r){"use strict";var e=r("XKFU"),o=r("AvRE")(!1);e(e.P,"String",{codePointAt:function(t){return o(this,t)}})},"oka+":function(t,n,r){r("GNAe"),t.exports=r("g3g5").parseInt},pIFo:function(t,n,r){"use strict";var e=r("y3w9"),o=r("S/j/"),i=r("ne8i"),u=r("RYi7"),c=r("A5AN"),a=r("Xxuz"),f=Math.max,s=Math.min,l=Math.floor,h=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g;r("IU+Z")("replace",2,function(t,n,r,v){return[function(e,o){var i=t(this),u=null==e?void 0:e[n];return void 0!==u?u.call(e,i,o):r.call(String(i),e,o)},function(t,n){var o=v(r,t,this,n);if(o.done)return o.value;var l=e(t),h=String(this),p="function"==typeof n;p||(n=String(n));var d=l.global;if(d){var g=l.unicode;l.lastIndex=0}for(var b=[];;){var m=a(l,h);if(null===m)break;if(b.push(m),!d)break;""===String(m[0])&&(l.lastIndex=c(h,i(l.lastIndex),g))}for(var _,k="",w=0,x=0;x=w&&(k+=h.slice(w,F)+K,w=F+S.length)}return k+h.slice(w)}];function y(t,n,e,i,u,c){var a=e+t.length,f=i.length,s=p;return void 0!==u&&(u=o(u),s=h),r.call(c,s,function(r,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,e);case"'":return n.slice(a);case"<":c=u[o.slice(1,-1)];break;default:var s=+o;if(0===s)return o;if(s>f){var h=l(s/10);return 0===h?o:h<=f?void 0===i[h-1]?o.charAt(1):i[h-1]+o.charAt(1):o}c=i[s-1]}return void 0===c?"":c})}})},"pp/T":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},qKs0:function(t,n,r){r("Btvt"),r("XfO3"),r("rGqo"),r("9AAn"),t.exports=r("g3g5").Map},qncB:function(t,n,r){var e=r("XKFU"),o=r("vhPU"),i=r("eeVq"),u=r("/e88"),c="["+u+"]",a=RegExp("^"+c+c+"*"),f=RegExp(c+c+"*$"),s=function(t,n,r){var o={},c=i(function(){return!!u[t]()||"\u200b\x85"!="\u200b\x85"[t]()}),a=o[t]=c?n(l):u[t];r&&(o[r]=a),e(e.P+e.F*c,"String",o)},l=s.trim=function(t,n){return t=String(o(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(f,"")),t};t.exports=s},quPj:function(t,n,r){var e=r("0/R4"),o=r("LZWt"),i=r("K0xU")("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[i])?!!n:"RegExp"==o(t))}},rGqo:function(t,n,r){for(var e=r("yt8O"),o=r("DVgA"),i=r("KroJ"),u=r("dyZX"),c=r("Mukb"),a=r("hPIQ"),f=r("K0xU"),s=f("iterator"),l=f("toStringTag"),h=a.Array,p={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},v=o(p),y=0;y1?arguments[1]:void 0,e=o(n.length),c=void 0===r?e:Math.min(o(r),e),a=String(t);return u?u.call(n,a,c):n.slice(c-a.length,c)===a}})},s5qY:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t)||t._t!==n)throw TypeError("Incompatible receiver, "+n+" required!");return t}},sMXx:function(t,n,r){"use strict";var e=r("Ugos");r("XKFU")({target:"RegExp",proto:!0,forced:e!==/./.exec},{exec:e})},sbF8:function(t,n,r){var e=r("XKFU"),o=r("nBIS"),i=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return o(t)&&i(t)<=9007199254740991}})},tUrg:function(t,n,r){"use strict";r("OGtf")("link",function(t){return function(n){return t(this,"a","href",n)}})},"tyy+":function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.G+e.F*(parseFloat!=o),{parseFloat:o})},upKx:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=[].copyWithin||function(t,n){var r=e(this),u=i(r.length),c=o(t,u),a=o(n,u),f=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===f?u:o(f,u))-a,u-c),l=1;for(a0;)a in r?r[c]=r[a]:delete r[c],c+=l,a+=l;return r}},vhPU:function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},vqGA:function(t,n,r){r("ioFf"),r("Btvt"),t.exports=r("g3g5").Symbol},vvmO:function(t,n,r){var e=r("LZWt");t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},w2a5:function(t,n,r){var e=r("aCFj"),o=r("ne8i"),i=r("d/Gc");t.exports=function(t){return function(n,r,u){var c,a=e(n),f=o(a.length),s=i(u,f);if(t&&r!=r){for(;f>s;)if((c=a[s++])!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===r)return t||s||0;return!t&&-1}}},wmvG:function(t,n,r){"use strict";var e=r("hswa").f,o=r("Kuth"),i=r("3Lyj"),u=r("m0Pp"),c=r("9gX7"),a=r("SlkY"),f=r("Afnz"),s=r("1TsA"),l=r("elZq"),h=r("nh4g"),p=r("Z6vF").fastKey,v=r("s5qY"),y=h?"_s":"size",d=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,f){var s=t(function(t,e){c(t,s,n,"_i"),t._t=n,t._i=o(null),t._f=void 0,t._l=void 0,t[y]=0,null!=e&&a(e,r,t[f],t)});return i(s.prototype,{clear:function(){for(var t=v(this,n),r=t._i,e=t._f;e;e=e.n)e.r=!0,e.p&&(e.p=e.p.n=void 0),delete r[e.i];t._f=t._l=void 0,t[y]=0},delete:function(t){var r=v(this,n),e=d(r,t);if(e){var o=e.n,i=e.p;delete r._i[e.i],e.r=!0,i&&(i.n=o),o&&(o.p=i),r._f==e&&(r._f=o),r._l==e&&(r._l=i),r[y]--}return!!e},forEach:function(t){v(this,n);for(var r,e=u(t,arguments.length>1?arguments[1]:void 0,3);r=r?r.n:this._f;)for(e(r.v,r.k,this);r&&r.r;)r=r.p},has:function(t){return!!d(v(this,n),t)}}),h&&e(s.prototype,"size",{get:function(){return v(this,n)[y]}}),s},def:function(t,n,r){var e,o,i=d(t,n);return i?i.v=r:(t._l=i={i:o=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=i),e&&(e.n=i),t[y]++,"F"!==o&&(t._i[o]=i)),t},getEntry:d,setStrong:function(t,n,r){f(t,n,function(t,r){this._t=v(t,n),this._k=r,this._l=void 0},function(){for(var t=this._k,n=this._l;n&&n.r;)n=n.p;return this._t&&(this._l=n=n?n.n:this._t._f)?s(0,"keys"==t?n.k:"values"==t?n.v:[n.k,n.v]):(this._t=void 0,s(1))},r?"entries":"values",!r,!0),l(n)}}},x8Yj:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S,"Math",{tanh:function(t){var n=o(t=+t),r=o(-t);return n==1/0?1:r==1/0?-1:(n-r)/(i(t)+i(-t))}})},x8ZO:function(t,n,r){var e=r("XKFU"),o=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,i=0,u=0,c=arguments.length,a=0;u0?(e=r/a)*e:r;return a===1/0?1/0:a*Math.sqrt(i)}})},xfY5:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("LZWt"),u=r("Xbzi"),c=r("apmT"),a=r("eeVq"),f=r("kJMx").f,s=r("EemH").f,l=r("hswa").f,h=r("qncB").trim,p=e.Number,v=p,y=p.prototype,d="Number"==i(r("Kuth")(y)),g="trim"in String.prototype,b=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){var r,e,o,i=(n=g?n.trim():h(n,3)).charCodeAt(0);if(43===i||45===i){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===i){switch(n.charCodeAt(1)){case 66:case 98:e=2,o=49;break;case 79:case 111:e=8,o=55;break;default:return+n}for(var u,a=n.slice(2),f=0,s=a.length;fo)return NaN;return parseInt(a,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(d?a(function(){y.valueOf.call(r)}):"Number"!=i(r))?u(new v(b(n)),r,p):b(n)};for(var m,_=r("nh4g")?f(v):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),k=0;_.length>k;k++)o(v,m=_[k])&&!o(p,m)&&l(p,m,s(v,m));p.prototype=y,y.constructor=p,r("KroJ")(e,"Number",p)}},xpql:function(t,n,r){t.exports=!r("nh4g")&&!r("eeVq")(function(){return 7!=Object.defineProperty(r("Iw71")("div"),"a",{get:function(){return 7}}).a})},y3w9:function(t,n,r){var e=r("0/R4");t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},yM4b:function(t,n,r){var e=r("K0xU")("toPrimitive"),o=Date.prototype;e in o||r("Mukb")(o,e,r("g4EE"))},ylqs:function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},yt8O:function(t,n,r){"use strict";var e=r("nGyu"),o=r("1TsA"),i=r("hPIQ"),u=r("aCFj");t.exports=r("Afnz")(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,o(1)):o(0,"keys"==n?r:"values"==n?t[r]:[r,t[r]])},"values"),i.Arguments=i.Array,e("keys"),e("values"),e("entries")},z2o2:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("seal",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},zRwo:function(t,n,r){var e=r("6FMO");t.exports=function(t,n){return new(e(t))(n)}},zhAb:function(t,n,r){var e=r("aagx"),o=r("aCFj"),i=r("w2a5")(!1),u=r("YTvA")("IE_PROTO");t.exports=function(t,n){var r,c=o(t),a=0,f=[];for(r in c)r!=u&&e(c,r)&&f.push(r);for(;n.length>a;)e(c,r=n[a++])&&(~i(f,r)||f.push(r));return f}}},[[1,0]]]); + +(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{0:function(n,t,e){n.exports=e("zUnb")},crnd:function(n,t){function e(n){return Promise.resolve().then(function(){var t=new Error("Cannot find module '"+n+"'");throw t.code="MODULE_NOT_FOUND",t})}e.keys=function(){return[]},e.resolve=e,n.exports=e,e.id="crnd"},zUnb:function(n,t,e){"use strict";e.r(t);var r=function(n,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,t){n.__proto__=t}||function(n,t){for(var e in t)t.hasOwnProperty(e)&&(n[e]=t[e])})(n,t)};function o(n,t){function e(){this.constructor=n}r(n,t),n.prototype=null===t?Object.create(t):(e.prototype=t.prototype,new e)}var i=function(){return(i=Object.assign||function(n){for(var t,e=1,r=arguments.length;e=0;u--)(o=n[u])&&(l=(i<3?o(l):i>3?o(t,e,l):o(t,e))||l);return i>3&&l&&Object.defineProperty(t,e,l),l}function u(n,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(n,t)}function s(n){var t="function"==typeof Symbol&&n[Symbol.iterator],e=0;return t?t.call(n):{next:function(){return n&&e>=n.length&&(n=void 0),{value:n&&n[e++],done:!n}}}}function a(n,t){var e="function"==typeof Symbol&&n[Symbol.iterator];if(!e)return n;var r,o,i=e.call(n),l=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)l.push(r.value)}catch(u){o={error:u}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(o)throw o.error}}return l}function c(){for(var n=[],t=0;t0?this._next(t.shift()):0===this.active&&this.hasCompleted&&this.destination.complete()},t}(W);function rn(n){return n}function on(){return function(n){return n.lift(new ln(n))}}var ln=function(){function n(n){this.connectable=n}return n.prototype.call=function(n,t){var e=this.connectable;e._refCount++;var r=new un(n,e),o=t.subscribe(r);return r.closed||(r.connection=e.connect()),o},n}(),un=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._refCount;if(t<=0)this.connection=null;else if(n._refCount=t-1,t>1)this.connection=null;else{var e=this.connection,r=n._connection;this.connection=null,!r||e&&r!==e||r.unsubscribe()}}else this.connection=null},t}(S),sn=function(n){function t(t,e){var r=n.call(this)||this;return r.source=t,r.subjectFactory=e,r._refCount=0,r._isComplete=!1,r}return o(t,n),t.prototype._subscribe=function(n){return this.getSubject().subscribe(n)},t.prototype.getSubject=function(){var n=this._subject;return n&&!n.isStopped||(this._subject=this.subjectFactory()),this._subject},t.prototype.connect=function(){var n=this._connection;return n||(this._isComplete=!1,(n=this._connection=new _).add(this.source.subscribe(new cn(this.getSubject(),this))),n.closed?(this._connection=null,n=_.EMPTY):this._connection=n),n},t.prototype.refCount=function(){return on()(this)},t}(I).prototype,an={operator:{value:null},_refCount:{value:0,writable:!0},_subject:{value:null,writable:!0},_connection:{value:null,writable:!0},_subscribe:{value:sn._subscribe},_isComplete:{value:sn._isComplete,writable:!0},getSubject:{value:sn.getSubject},connect:{value:sn.connect},refCount:{value:sn.refCount}},cn=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._error=function(t){this._unsubscribe(),n.prototype._error.call(this,t)},t.prototype._complete=function(){this.connectable._isComplete=!0,this._unsubscribe(),n.prototype._complete.call(this)},t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._connection;n._refCount=0,n._subject=null,n._connection=null,t&&t.unsubscribe()}},t}(V);function fn(){return new H}function hn(n){for(var t in n)if(n[t]===hn)return t;throw Error("Could not find renamed property on target object.")}var pn=hn({ngComponentDef:hn}),dn=hn({ngInjectableDef:hn}),gn=hn({ngInjectorDef:hn}),vn=hn({ngModuleDef:hn}),yn=hn({__NG_ELEMENT_ID__:hn});function mn(n){return{providedIn:n.providedIn||null,factory:n.factory,value:void 0}}function bn(n){return n.hasOwnProperty(dn)?n[dn]:null}function _n(n){return n.hasOwnProperty(gn)?n[gn]:null}var wn=function(){function n(n,t){this._desc=n,this.ngMetadataName="InjectionToken",this.ngInjectableDef=void 0!==t?mn({providedIn:t.providedIn||"root",factory:t.factory}):void 0}return n.prototype.toString=function(){return"InjectionToken "+this._desc},n}(),Cn="__parameters__";function En(n,t,e){var r=function(n){return function(){for(var t=[],e=0;e=Yn?e:e[ot]}function Nt(n){return n[St]}function Dt(n){var t=Nt(n);return t?Array.isArray(t)?t:t.lViewData:null}function Mt(n){return 32767&n}function Vt(n,t){for(var e=n>>16,r=t;e>0;)r=r[pt],e--;return r}var Ht,Rt,jt,Lt,zt,Ft,Bt,Ut,Gt=("undefined"!=typeof requestAnimationFrame&&requestAnimationFrame||setTimeout).bind(Sn);function Zt(){return Ht}function qt(){return Rt}function $t(){return jt}function Qt(n){jt=n}function Wt(n,t){jt=n,Ut=t}function Kt(){return Lt}function Jt(n){Lt=n}function Yt(){return zt}function Xt(){return Bt}function ne(){return Ut}var te=!1;function ee(){return te}function re(n){te=n}var oe=!0;function ie(n){oe=n}function le(n,t){var e=Ut;return zt=n&&n[Xn],Bt=n&&1==(1&n[nt]),oe=n&&zt.firstTemplatePass,Ht=n&&n[ct],jt=t,Lt=!0,Ut=n,e&&(e[rt]=Ft),Ft=n&&n[rt],e}function ue(n,t){t||(te||yt(Ut,zt.viewHooks,zt.viewCheckHooks,Bt),Ut[nt]&=-6),Ut[nt]|=16,Ut[lt]=zt.bindingStartIndex,le(n,null)}var se=!1;function ae(n){var t=se;return se=n,t}var ce=255,fe=0;function he(n,t){var e=de(n,t);if(-1!==e)return e;var r=t[Xn];r.firstTemplatePass&&(n.injectorIndex=t.length,pe(r.data,n),pe(t,null),pe(r.blueprint,null));var o=ge(n,t),i=Mt(o),l=Vt(o,t),u=n.injectorIndex;if(o!==Wn)for(var s=l[Xn].data,a=0;a<8;a++)t[u+a]=l[i+a]|s[i+a];return t[u+$n]=o,u}function pe(n,t){n.push(0,0,0,0,0,0,0,0,t)}function de(n,t){return-1===n.injectorIndex||n.parent&&n.parent.injectorIndex===n.injectorIndex||null==t[n.injectorIndex+$n]?-1:n.injectorIndex}function ge(n,t){if(n.parent&&-1!==n.parent.injectorIndex)return n.parent.injectorIndex;for(var e=t[it],r=1;e&&-1===e.injectorIndex;)e=(t=t[pt])[it],r++;return e?e.injectorIndex|r<<16|(e&&3===e.type?32768:0):-1}var ve={};function ye(n,t,e,r){var o=t[Xn],i=o.data[n+qn],l=i.flags,u=i.providerIndexes,s=o.data,a=!1;(null==r&&function(n){return 4096==(4096&n.flags)}(i)&&se||null!=r&&r!=o&&(null==o.node||3===o.node.type))&&(a=!0);for(var c=65535&u,f=l>>16,h=4095&l,p=a?c:c+(u>>16);p=f&&d.type===e)return me(s,t,p,i)}return ve}function me(n,t,e,r){var o,i=t[e];if(null!=(o=i)&&"object"==typeof o&&Object.getPrototypeOf(o)==Jn){var l=i;if(l.resolving)throw new Error("Circular dep for "+At(n[e]));var u=ae(l.canSeeViewProviders);l.resolving=!0;var s=void 0;l.injectImpl&&(s=Bn(l.injectImpl));var a=$t(),c=ne();Wt(r,t);try{i=t[e]=l.factory(null,n,t,r)}finally{l.injectImpl&&Bn(s),ae(u),l.resolving=!1,Wt(a,c)}}return i}function be(n,t,e){var r=64&n,o=32&n;return!!((128&n?r?o?e[t+7]:e[t+6]:o?e[t+5]:e[t+4]:r?o?e[t+3]:e[t+2]:o?e[t+1]:e[t])&1< ");else if("object"==typeof t){var o=[];for(var i in t)if(t.hasOwnProperty(i)){var l=t[i];o.push(i+":"+("string"==typeof l?JSON.stringify(l):Dn(l)))}r="{"+o.join(", ")+"}"}return"StaticInjectorError"+(e?"("+e+")":"")+"["+r+"]: "+n.replace(ze,"\n ")}function Ze(n,t){return new Error(Ge(n,t))}var qe=function(){return function(){}}(),$e=function(){return function(){}}(),Qe="ngProjectAs";function We(n){return!!n.listen}var Ke={createRenderer:function(n,t){return document}},Je=[];function Ye(n){for(var t=n[it];t&&2===t.type;)t=(n=n[tt])[it];return n}function Xe(n,t,e,r,o){0===n?We(t)?t.insertBefore(e,r,o):e.insertBefore(r,o,!0):1===n?We(t)?t.removeChild(e,r):e.removeChild(r):2===n&&t.destroyNode(r)}function nr(n){var t=n[Xn].childIndex;return-1===t?null:n[t]}function tr(n,t){var e;return n.length>=Yn&&(e=n[it])&&2===e.type?function(t,e){if(-1===t.index){var r=n[ht];return r>-1?n[tt][r]:null}return n[tt][t.parent.index]}(e):n[tt]===t?null:n[tt]}function er(n){if(n.length>=Yn){var t=n;!function(n){var t=n[Xn].cleanup;if(null!=t){for(var e=0;e=Yn?t[Xn].childIndex>-1&&(e=nr(t)):t[Ot].length&&(e=t[Ot][0]),null==e){for(;t&&!t[et]&&t!==n;)er(t),t=tr(t,n);er(t||n),e=t&&t[et]}t=e}}(n),n[nt]|=32},n.prototype.onDestroy=function(n){var t,e;e=n,function(n){return n[ut]||(n[ut]=[])}(t=this._view).push(e),t[Xn].firstTemplatePass&&function(n){return n[Xn].cleanup||(n[Xn].cleanup=[])}(t).push(t[ut].length-1,null)},n.prototype.markForCheck=function(){!function(n){for(var t=n;t&&!(64&t[nt]);)t[nt]|=4,t=t[tt];var e,r,o;t[nt]|=4,o=0===(e=t[st]).flags,e.flags|=1,o&&e.clean==or&&(e.clean=new Promise(function(n){return r=n}),e.scheduler(function(){if(1&e.flags&&(e.flags&=-2,mr(e)),2&e.flags){e.flags&=-3;var n=e.playerHandler;n&&n.flushPlayers()}e.clean=or,r(null)}))}(this._view)},n.prototype.detach=function(){this._view[nt]&=-9},n.prototype.reattach=function(){this._view[nt]|=8},n.prototype.detectChanges=function(){var n=qt();n.begin&&n.begin(),br(this.context),n.end&&n.end()},n.prototype.checkNoChanges=function(){!function(n){re(!0);try{br(n)}finally{re(!1)}}(this.context)},n.prototype.attachToViewContainerRef=function(n){this._viewContainerRef=n},n.prototype.detachFromAppRef=function(){this._appRef=null},n.prototype.attachToAppRef=function(n){this._appRef=n},n.prototype._lookUpContext=function(){return this._context=this._view[tt][this._componentIndex]},n}());function Or(n,t,e,r,o){var i=e[Xn],l=function(n,t,e){var r=$t();n.firstTemplatePass&&(e.providersResolver&&e.providersResolver(e),function(n,t,e){var o=-(r.index-Yn),i=n.data.length-(65535&r.providerIndexes);(n.expandoInstructions||(n.expandoInstructions=[])).push(o,i,1)}(n),function(n,t,e,r){n.data.push(e);var o=new Kn(r,function(n){return null!==n.template}(e),null);n.blueprint.push(o),t.push(o),function(n,t){n.expandoInstructions.push(t.hostBindings||Ee),t.hostVars&&n.expandoInstructions.push(t.hostVars)}(n,e)}(n,t,e,e.factory));var o=me(n.data,t,t.length-1,r);return function(n,t,e,r){var o=It(t,n);Ce(e,n),o&&Ce(o,n),null!=r.attributes&&3==t.type&&function(n,t){for(var e=Zt(),r=We(e),o=0;o>16,r=e+(4095&n),o=e;o',!this.inertBodyElement.querySelector||this.inertBodyElement.querySelector("svg")?(this.inertBodyElement.innerHTML='

',this.getInertBodyElement=this.inertBodyElement.querySelector&&this.inertBodyElement.querySelector("svg img")&&function(){try{return!!window.DOMParser}catch(n){return!1}}()?this.getInertBodyElement_DOMParser:this.getInertBodyElement_InertDocument):this.getInertBodyElement=this.getInertBodyElement_XHR}return n.prototype.getInertBodyElement_XHR=function(n){n=""+n+"";try{n=encodeURI(n)}catch(r){return null}var t=new XMLHttpRequest;t.responseType="document",t.open("GET","data:text/html;charset=utf-8,"+n,!1),t.send(void 0);var e=t.response.body;return e.removeChild(e.firstChild),e},n.prototype.getInertBodyElement_DOMParser=function(n){n=""+n+"";try{var t=(new window.DOMParser).parseFromString(n,"text/html").body;return t.removeChild(t.firstChild),t}catch(e){return null}},n.prototype.getInertBodyElement_InertDocument=function(n){var t=this.inertDocument.createElement("template");return"content"in t?(t.innerHTML=n,t):(this.inertBodyElement.innerHTML=n,this.defaultDoc.documentMode&&this.stripCustomNsAttrs(this.inertBodyElement),this.inertBodyElement)},n.prototype.stripCustomNsAttrs=function(n){for(var t=n.attributes,e=t.length-1;0"),!0},n.prototype.endElement=function(n){var t=n.nodeName.toLowerCase();Oo.hasOwnProperty(t)&&!wo.hasOwnProperty(t)&&(this.buf.push(""))},n.prototype.chars=function(n){this.buf.push(No(n))},n.prototype.checkClobberedElement=function(n,t){if(t&&(n.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)===Node.DOCUMENT_POSITION_CONTAINED_BY)throw new Error("Failed to sanitize html because the element is clobbered: "+n.outerHTML);return t},n}(),Io=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Po=/([^\#-~ |!])/g;function No(n){return n.replace(/&/g,"&").replace(Io,function(n){return"&#"+(1024*(n.charCodeAt(0)-55296)+(n.charCodeAt(1)-56320)+65536)+";"}).replace(Po,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}function Do(n){return"content"in n&&function(n){return n.nodeType===Node.ELEMENT_NODE&&"TEMPLATE"===n.nodeName}(n)?n.content:null}var Mo={provide:Zr,useFactory:function(){return new eo},deps:[]},Vo=function(n){function t(t,e){var r=n.call(this)||this;return r._bootstrapComponents=[],r.destroyCbs=[],r._bootstrapComponents=(t[vn]||null).bootstrap,r.injector=function(n,t,e){return void 0===t&&(t=null),void 0===e&&(e=null),t=t||Dr(),new Mr(n,e,t)}(t,e,[Mo,{provide:qe,useValue:r}]),r.instance=r.injector.get(t),r.componentFactoryResolver=new eo,r}return o(t,n),t.prototype.destroy=function(){this.destroyCbs.forEach(function(n){return n()}),this.destroyCbs=null},t.prototype.onDestroy=function(n){this.destroyCbs.push(n)},t}(qe);!function(n){function t(t){var e=n.call(this)||this;return e.moduleType=t,e}o(t,n),t.prototype.create=function(n){return new Vo(this.moduleType,n)}}($e);var Ho=function(n){function t(t){void 0===t&&(t=!1);var e=n.call(this)||this;return e.__isAsync=t,e}return o(t,n),t.prototype.emit=function(t){n.prototype.next.call(this,t)},t.prototype.subscribe=function(t,e,r){var o,i=function(n){return null},l=function(){return null};t&&"object"==typeof t?(o=this.__isAsync?function(n){setTimeout(function(){return t.next(n)})}:function(n){t.next(n)},t.error&&(i=this.__isAsync?function(n){setTimeout(function(){return t.error(n)})}:function(n){t.error(n)}),t.complete&&(l=this.__isAsync?function(){setTimeout(function(){return t.complete()})}:function(){t.complete()})):(o=this.__isAsync?function(n){setTimeout(function(){return t(n)})}:function(n){t(n)},e&&(i=this.__isAsync?function(n){setTimeout(function(){return e(n)})}:function(n){e(n)}),r&&(l=this.__isAsync?function(){setTimeout(function(){return r()})}:function(){r()}));var u=n.prototype.subscribe.call(this,o,i,l);return t instanceof _&&t.add(u),u},t}(H),Ro=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return jo(n,Qr)},n}(),jo=Ee,Lo=function(n){return n[n.NONE=0]="NONE",n[n.HTML=1]="HTML",n[n.STYLE=2]="STYLE",n[n.SCRIPT=3]="SCRIPT",n[n.URL=4]="URL",n[n.RESOURCE_URL=5]="RESOURCE_URL",n}({}),zo=function(){return function(){}}(),Fo=new RegExp("^([-,.\"'%_!# a-zA-Z0-9]+|(?:(?:matrix|translate|scale|rotate|skew|perspective)(?:X|Y|3d)?|(?:rgb|hsl)a?|(?:repeating-)?(?:linear|radial)-gradient|(?:calc|attr))\\([-0-9.%, #a-zA-Z]+\\))$","g"),Bo=/^url\(([^)]+)\)$/;Function,String,String;var Uo="ngDebugContext",Go="ngOriginalError",Zo="ngErrorLogger";function qo(n){return n[Uo]}function $o(n){return n[Go]}function Qo(n){for(var t=[],e=1;e0&&(o=setTimeout(function(){r._callbacks=r._callbacks.filter(function(n){return n.timeoutId!==o}),n(r._didWork,r.getPendingTasks())},t)),this._callbacks.push({doneCb:n,timeoutId:o,updateCb:e})},n.prototype.whenStable=function(n,t,e){if(e&&!this.taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/dist/task-tracking.js" loaded?');this.addCallback(n,t,e),this._runCallbacksIfReady()},n.prototype.getPendingRequestCount=function(){return this._pendingCount},n.prototype.findProviders=function(n,t,e){return[]},n}(),ki=function(){function n(){this._applications=new Map,Si.addToWindow(this)}return n.prototype.registerApplication=function(n,t){this._applications.set(n,t)},n.prototype.unregisterApplication=function(n){this._applications.delete(n)},n.prototype.unregisterAllApplications=function(){this._applications.clear()},n.prototype.getTestability=function(n){return this._applications.get(n)||null},n.prototype.getAllTestabilities=function(){return Array.from(this._applications.values())},n.prototype.getAllRootElements=function(){return Array.from(this._applications.keys())},n.prototype.findTestabilityInTree=function(n,t){return void 0===t&&(t=!0),Si.findTestabilityInTree(this,n,t)},l([u("design:paramtypes",[])],n)}(),Si=new(function(){function n(){}return n.prototype.addToWindow=function(n){},n.prototype.findTestabilityInTree=function(n,t,e){return null},n}()),Ai=new wn("AllowMultipleToken"),Ti=function(){return function(n,t){this.name=n,this.token=t}}();function Ii(n,t,e){void 0===e&&(e=[]);var r="Platform: "+t,o=new wn(r);return function(t){void 0===t&&(t=[]);var i=Pi();if(!i||i.injector.get(Ai,!1))if(n)n(e.concat(t).concat({provide:o,useValue:!0}));else{var l=e.concat(t).concat({provide:o,useValue:!0});!function(n){if(Ei&&!Ei.destroyed&&!Ei.injector.get(Ai,!1))throw new Error("There can be only one platform. Destroy the previous one to create a new one.");Ei=n.get(Ni);var t=n.get(ri,null);t&&t.forEach(function(n){return n()})}(Ne.create({providers:l,name:r}))}return function(n){var t=Pi();if(!t)throw new Error("No platform exists!");if(!t.injector.get(n,null))throw new Error("A platform with a different configuration has been created. Please destroy it first.");return t}(o)}}function Pi(){return Ei&&!Ei.destroyed?Ei:null}var Ni=function(){function n(n){this._injector=n,this._modules=[],this._destroyListeners=[],this._destroyed=!1}return n.prototype.bootstrapModuleFactory=function(n,t){var e,r=this,o="noop"===(e=t?t.ngZone:void 0)?new xi:("zone.js"===e?void 0:e)||new yi({enableLongStackTrace:ho()}),i=[{provide:yi,useValue:o}];return o.run(function(){var t=Ne.create({providers:i,parent:r.injector,name:n.moduleType.name}),e=n.create(t),l=e.injector.get(Wo,null);if(!l)throw new Error("No ErrorHandler. Is platform module (BrowserModule) included?");return e.onDestroy(function(){return Vi(r._modules,e)}),o.runOutsideAngular(function(){return o.onError.subscribe({next:function(n){l.handleError(n)}})}),function(n,t,o){try{var i=((l=e.injector.get(Xo)).runInitializers(),l.donePromise.then(function(){return r._moduleDoBootstrap(e),e}));return Ko(i)?i.catch(function(e){throw t.runOutsideAngular(function(){return n.handleError(e)}),e}):i}catch(u){throw t.runOutsideAngular(function(){return n.handleError(u)}),u}var l}(l,o)})},n.prototype.bootstrapModule=function(n,t){var e=this;void 0===t&&(t=[]);var r=Di({},t);return function(n,t,e){return n.get(fi).createCompiler([t]).compileModuleAsync(e)}(this.injector,r,n).then(function(n){return e.bootstrapModuleFactory(n,r)})},n.prototype._moduleDoBootstrap=function(n){var t=n.injector.get(Mi);if(n._bootstrapComponents.length>0)n._bootstrapComponents.forEach(function(n){return t.bootstrap(n)});else{if(!n.instance.ngDoBootstrap)throw new Error("The module "+Dn(n.instance.constructor)+' was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. Please define one of these.');n.instance.ngDoBootstrap(t)}this._modules.push(n)},n.prototype.onDestroy=function(n){this._destroyListeners.push(n)},Object.defineProperty(n.prototype,"injector",{get:function(){return this._injector},enumerable:!0,configurable:!0}),n.prototype.destroy=function(){if(this._destroyed)throw new Error("The platform has already been destroyed!");this._modules.slice().forEach(function(n){return n.destroy()}),this._destroyListeners.forEach(function(n){return n()}),this._destroyed=!0},Object.defineProperty(n.prototype,"destroyed",{get:function(){return this._destroyed},enumerable:!0,configurable:!0}),n}();function Di(n,t){return Array.isArray(t)?t.reduce(Di,n):i({},n,t)}var Mi=function(){function n(n,t,e,r,o,i){var l=this;this._zone=n,this._console=t,this._injector=e,this._exceptionHandler=r,this._componentFactoryResolver=o,this._initStatus=i,this._bootstrapListeners=[],this._views=[],this._runningTick=!1,this._enforceNoNewChanges=!1,this._stable=!0,this.componentTypes=[],this.components=[],this._enforceNoNewChanges=ho(),this._zone.onMicrotaskEmpty.subscribe({next:function(){l._zone.run(function(){l.tick()})}});var u=new I(function(n){l._stable=l._zone.isStable&&!l._zone.hasPendingMacrotasks&&!l._zone.hasPendingMicrotasks,l._zone.runOutsideAngular(function(){n.next(l._stable),n.complete()})}),s=new I(function(n){var t;l._zone.runOutsideAngular(function(){t=l._zone.onStable.subscribe(function(){yi.assertNotInAngularZone(),Pn(function(){l._stable||l._zone.hasPendingMacrotasks||l._zone.hasPendingMicrotasks||(l._stable=!0,n.next(!0))})})});var e=l._zone.onUnstable.subscribe(function(){yi.assertInAngularZone(),l._stable&&(l._stable=!1,l._zone.runOutsideAngular(function(){n.next(!1)}))});return function(){t.unsubscribe(),e.unsubscribe()}});this.isStable=function(){for(var n=[],t=0;t1&&"number"==typeof n[n.length-1]&&(r=n.pop())):"number"==typeof i&&(r=n.pop()),null===o&&1===n.length&&n[0]instanceof I?n[0]:function(n){return void 0===n&&(n=Number.POSITIVE_INFINITY),function n(t,e,r){return void 0===r&&(r=Number.POSITIVE_INFINITY),"function"==typeof e?function(o){return o.pipe(n(function(n,r){return nn(t(n,r)).pipe(K(function(t,o){return e(n,t,r,o)}))},r))}:("number"==typeof e&&(r=e),function(n){return n.lift(new tn(t,r))})}(rn,n)}(r)(X(n,o))}(u,s.pipe(function(n){return on()((t=fn,function(n){var e;e="function"==typeof t?t:function(){return t};var r=Object.create(n,an);return r.source=n,r.subjectFactory=e,r})(n));var t}))}var t;return t=n,n.prototype.bootstrap=function(n,t){var e,r=this;if(!this._initStatus.done)throw new Error("Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.");e=n instanceof Fr?n:this._componentFactoryResolver.resolveComponentFactory(n),this.componentTypes.push(e.componentType);var o=e instanceof $r?null:this._injector.get(qe),i=e.create(Ne.NULL,[],t||e.selector,o);i.onDestroy(function(){r._unloadComponent(i)});var l=i.injector.get(Oi,null);return l&&i.injector.get(ki).registerApplication(i.location.nativeElement,l),this._loadComponent(i),ho()&&this._console.log("Angular is running in the development mode. Call enableProdMode() to enable the production mode."),i},n.prototype.tick=function(){var n=this;if(this._runningTick)throw new Error("ApplicationRef.tick is called recursively");var e=t._tickScope();try{this._runningTick=!0,this._views.forEach(function(n){return n.detectChanges()}),this._enforceNoNewChanges&&this._views.forEach(function(n){return n.checkNoChanges()})}catch(r){this._zone.runOutsideAngular(function(){return n._exceptionHandler.handleError(r)})}finally{this._runningTick=!1,vi(e)}},n.prototype.attachView=function(n){var t=n;this._views.push(t),t.attachToAppRef(this)},n.prototype.detachView=function(n){var t=n;Vi(this._views,t),t.detachFromAppRef()},n.prototype._loadComponent=function(n){this.attachView(n.hostView),this.tick(),this.components.push(n),this._injector.get(ii,[]).concat(this._bootstrapListeners).forEach(function(t){return t(n)})},n.prototype._unloadComponent=function(n){this.detachView(n.hostView),Vi(this.components,n)},n.prototype.ngOnDestroy=function(){this._views.slice().forEach(function(n){return n.destroy()})},Object.defineProperty(n.prototype,"viewCount",{get:function(){return this._views.length},enumerable:!0,configurable:!0}),n._tickScope=gi("ApplicationRef#tick()"),n}();function Vi(n,t){var e=n.indexOf(t);e>-1&&n.splice(e,1)}var Hi,Ri=function(){function n(){this.dirty=!0,this._results=[],this.changes=new Ho,this.length=0}return n.prototype.map=function(n){return this._results.map(n)},n.prototype.filter=function(n){return this._results.filter(n)},n.prototype.find=function(n){return this._results.find(n)},n.prototype.reduce=function(n,t){return this._results.reduce(n,t)},n.prototype.forEach=function(n){this._results.forEach(n)},n.prototype.some=function(n){return this._results.some(n)},n.prototype.toArray=function(){return this._results.slice()},n.prototype[In()]=function(){return this._results[In()]()},n.prototype.toString=function(){return this._results.toString()},n.prototype.reset=function(n){this._results=function n(t){return t.reduce(function(t,e){var r=Array.isArray(e)?n(e):e;return t.concat(r)},[])}(n),this.dirty=!1,this.length=this._results.length,this.last=this._results[this.length-1],this.first=this._results[0]},n.prototype.notifyOnChanges=function(){this.changes.emit(this)},n.prototype.setDirty=function(){this.dirty=!0},n.prototype.destroy=function(){this.changes.complete(),this.changes.unsubscribe()},n}(),ji=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Li(n,Qr)},n}(),Li=Ee,zi=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Fi()},n}(),Fi=function(){for(var n=[],t=0;t-1}(r)||"root"===o.providedIn&&r._def.isRoot))){var c=n._providers.length;return n._def.providersByKey[t.tokenKey]={flags:5120,value:u.factory,deps:[],index:c,token:t.token},n._providers[c]=fu,n._providers[c]=yu(n,n._def.providersByKey[t.tokenKey])}return 4&t.flags?e:n._parent.get(t.token,e)}finally{Fn(i)}}function yu(n,t){var e;switch(201347067&t.flags){case 512:e=function(n,t,e){var r=e.length;switch(r){case 0:return new t;case 1:return new t(vu(n,e[0]));case 2:return new t(vu(n,e[0]),vu(n,e[1]));case 3:return new t(vu(n,e[0]),vu(n,e[1]),vu(n,e[2]));default:for(var o=new Array(r),i=0;i=e.length)&&(t=e.length-1),t<0)return null;var r=e[t];return r.viewContainerParent=null,Cu(e,t),Cl.dirtyParentQueries(r),_u(r),r}function bu(n,t,e){var r=t?Fl(t,t.def.lastRenderRootNode):n.renderElement,o=e.renderer.parentNode(r),i=e.renderer.nextSibling(r);Wl(e,2,o,i,void 0)}function _u(n){Wl(n,3,null,null,void 0)}function wu(n,t,e){t>=n.length?n.push(e):n.splice(t,0,e)}function Cu(n,t){t>=n.length-1?n.pop():n.splice(t,1)}var Eu=new Object;function xu(n,t,e,r,o,i){return new Ou(n,t,e,r,o,i)}var Ou=function(n){function t(t,e,r,o,i,l){var u=n.call(this)||this;return u.selector=t,u.componentType=e,u._inputs=o,u._outputs=i,u.ngContentSelectors=l,u.viewDefFactory=r,u}return o(t,n),Object.defineProperty(t.prototype,"inputs",{get:function(){var n=[],t=this._inputs;for(var e in t)n.push({propName:e,templateName:t[e]});return n},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"outputs",{get:function(){var n=[];for(var t in this._outputs)n.push({propName:t,templateName:this._outputs[t]});return n},enumerable:!0,configurable:!0}),t.prototype.create=function(n,t,e,r){if(!r)throw new Error("ngModule should be provided");var o=Ql(this.viewDefFactory),i=o.nodes[0].element.componentProvider.nodeIndex,l=Cl.createRootView(n,t||[],e,o,r,Eu),u=bl(l,i).instance;return e&&l.renderer.setAttribute(ml(l,0).renderElement,"ng-version",to.full),new ku(l,new Iu(l),u)},t}(Fr),ku=function(n){function t(t,e,r){var o=n.call(this)||this;return o._view=t,o._viewRef=e,o._component=r,o._elDef=o._view.def.nodes[0],o.hostView=e,o.changeDetectorRef=e,o.instance=r,o}return o(t,n),Object.defineProperty(t.prototype,"location",{get:function(){return new Qr(ml(this._view,this._elDef.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"componentType",{get:function(){return this._component.constructor},enumerable:!0,configurable:!0}),t.prototype.destroy=function(){this._viewRef.destroy()},t.prototype.onDestroy=function(n){this._viewRef.onDestroy(n)},t}(zr);function Su(n,t,e){return new Au(n,t,e)}var Au=function(){function n(n,t,e){this._view=n,this._elDef=t,this._data=e,this._embeddedViews=[]}return Object.defineProperty(n.prototype,"element",{get:function(){return new Qr(this._data.renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"parentInjector",{get:function(){for(var n=this._view,t=this._elDef.parent;!t&&n;)t=zl(n),n=n.parent;return n?new Mu(n,t):new Mu(this._view,null)},enumerable:!0,configurable:!0}),n.prototype.clear=function(){for(var n=this._embeddedViews.length-1;n>=0;n--){var t=mu(this._data,n);Cl.destroyView(t)}},n.prototype.get=function(n){var t=this._embeddedViews[n];if(t){var e=new Iu(t);return e.attachToViewContainerRef(this),e}return null},Object.defineProperty(n.prototype,"length",{get:function(){return this._embeddedViews.length},enumerable:!0,configurable:!0}),n.prototype.createEmbeddedView=function(n,t,e){var r=n.createEmbeddedView(t||{});return this.insert(r,e),r},n.prototype.createComponent=function(n,t,e,r,o){var i=e||this.parentInjector;o||n instanceof $r||(o=i.get(qe));var l=n.create(i,r,void 0,o);return this.insert(l.hostView,t),l},n.prototype.insert=function(n,t){if(n.destroyed)throw new Error("Cannot insert a destroyed View in a ViewContainer!");var e,r,o,i,l=n;return i=(e=this._data).viewContainer._embeddedViews,null==(r=t)&&(r=i.length),(o=l._view).viewContainerParent=this._view,wu(i,r,o),function(n,t){var e=Ll(t);if(e&&e!==n&&!(16&t.state)){t.state|=16;var r=e.template._projectedViews;r||(r=e.template._projectedViews=[]),r.push(t),function(n,e){if(!(4&e.flags)){t.parent.def.nodeFlags|=4,e.flags|=4;for(var r=e.parent;r;)r.childFlags|=4,r=r.parent}}(0,t.parentNodeDef)}}(e,o),Cl.dirtyParentQueries(o),bu(e,r>0?i[r-1]:null,o),l.attachToViewContainerRef(this),n},n.prototype.move=function(n,t){if(n.destroyed)throw new Error("Cannot move a destroyed View in a ViewContainer!");var e,r,o,i,l,u=this._embeddedViews.indexOf(n._view);return o=t,l=(i=(e=this._data).viewContainer._embeddedViews)[r=u],Cu(i,r),null==o&&(o=i.length),wu(i,o,l),Cl.dirtyParentQueries(l),_u(l),bu(e,o>0?i[o-1]:null,l),n},n.prototype.indexOf=function(n){return this._embeddedViews.indexOf(n._view)},n.prototype.remove=function(n){var t=mu(this._data,n);t&&Cl.destroyView(t)},n.prototype.detach=function(n){var t=mu(this._data,n);return t?new Iu(t):null},n}();function Tu(n){return new Iu(n)}var Iu=function(){function n(n){this._view=n,this._viewContainerRef=null,this._appRef=null}return Object.defineProperty(n.prototype,"rootNodes",{get:function(){return Wl(this._view,0,void 0,void 0,n=[]),n;var n},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"context",{get:function(){return this._view.context},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"destroyed",{get:function(){return 0!=(128&this._view.state)},enumerable:!0,configurable:!0}),n.prototype.markForCheck=function(){Hl(this._view)},n.prototype.detach=function(){this._view.state&=-5},n.prototype.detectChanges=function(){var n=this._view.root.rendererFactory;n.begin&&n.begin();try{Cl.checkAndUpdateView(this._view)}finally{n.end&&n.end()}},n.prototype.checkNoChanges=function(){Cl.checkNoChangesView(this._view)},n.prototype.reattach=function(){this._view.state|=4},n.prototype.onDestroy=function(n){this._view.disposables||(this._view.disposables=[]),this._view.disposables.push(n)},n.prototype.destroy=function(){this._appRef?this._appRef.detachView(this):this._viewContainerRef&&this._viewContainerRef.detach(this._viewContainerRef.indexOf(this)),Cl.destroyView(this._view)},n.prototype.detachFromAppRef=function(){this._appRef=null,_u(this._view),Cl.dirtyParentQueries(this._view)},n.prototype.attachToAppRef=function(n){if(this._viewContainerRef)throw new Error("This view is already attached to a ViewContainer!");this._appRef=n},n.prototype.attachToViewContainerRef=function(n){if(this._appRef)throw new Error("This view is already attached directly to the ApplicationRef!");this._viewContainerRef=n},n}();function Pu(n,t){return new Nu(n,t)}var Nu=function(n){function t(t,e){var r=n.call(this)||this;return r._parentView=t,r._def=e,r}return o(t,n),t.prototype.createEmbeddedView=function(n){return new Iu(Cl.createEmbeddedView(this._parentView,this._def,this._def.element.template,n))},Object.defineProperty(t.prototype,"elementRef",{get:function(){return new Qr(ml(this._parentView,this._def.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),t}(Ro);function Du(n,t){return new Mu(n,t)}var Mu=function(){function n(n,t){this.view=n,this.elDef=t}return n.prototype.get=function(n,t){return void 0===t&&(t=Ne.THROW_IF_NOT_FOUND),Cl.resolveDep(this.view,this.elDef,!!this.elDef&&0!=(33554432&this.elDef.flags),{flags:0,token:n,tokenKey:Al(n)},t)},n}();function Vu(n,t){var e=n.def.nodes[t];if(1&e.flags){var r=ml(n,e.nodeIndex);return e.element.template?r.template:r.renderElement}if(2&e.flags)return yl(n,e.nodeIndex).renderText;if(20240&e.flags)return bl(n,e.nodeIndex).instance;throw new Error("Illegal state: read nodeValue for node index "+t)}function Hu(n){return new Ru(n.renderer)}var Ru=function(){function n(n){this.delegate=n}return n.prototype.selectRootElement=function(n){return this.delegate.selectRootElement(n)},n.prototype.createElement=function(n,t){var e=a(tu(t),2),r=this.delegate.createElement(e[1],e[0]);return n&&this.delegate.appendChild(n,r),r},n.prototype.createViewRoot=function(n){return n},n.prototype.createTemplateAnchor=function(n){var t=this.delegate.createComment("");return n&&this.delegate.appendChild(n,t),t},n.prototype.createText=function(n,t){var e=this.delegate.createText(t);return n&&this.delegate.appendChild(n,e),e},n.prototype.projectNodes=function(n,t){for(var e=0;e0,t.provider.value,t.provider.deps);if(t.outputs.length)for(var r=0;r0,r=t.provider;switch(201347067&t.flags){case 512:return es(n,t.parent,e,r.value,r.deps);case 1024:return function(n,t,e,r,o){var i=o.length;switch(i){case 0:return r();case 1:return r(os(n,t,e,o[0]));case 2:return r(os(n,t,e,o[0]),os(n,t,e,o[1]));case 3:return r(os(n,t,e,o[0]),os(n,t,e,o[1]),os(n,t,e,o[2]));default:for(var l=Array(i),u=0;u0)a=g,_s(g)||(c=g);else for(;a&&d===a.nodeIndex+a.childCount;){var m=a.parent;m&&(m.childFlags|=a.childFlags,m.childMatchedQueries|=a.childMatchedQueries),c=(a=m)&&_s(a)?a.renderParent:a}}return{factory:null,nodeFlags:l,rootNodeFlags:u,nodeMatchedQueries:s,flags:n,nodes:t,updateDirectives:e||kl,updateRenderer:r||kl,handleEvent:function(n,e,r,o){return t[e].element.handleEvent(n,r,o)},bindingCount:o,outputCount:i,lastRenderRootNode:p}}function _s(n){return 0!=(1&n.flags)&&null===n.element.name}function ws(n,t,e){var r=t.element&&t.element.template;if(r){if(!r.lastRenderRootNode)throw new Error("Illegal State: Embedded templates without nodes are not allowed!");if(r.lastRenderRootNode&&16777216&r.lastRenderRootNode.flags)throw new Error("Illegal State: Last root node of a template can't have embedded views, at index "+t.nodeIndex+"!")}if(20224&t.flags&&0==(1&(n?n.flags:0)))throw new Error("Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index "+t.nodeIndex+"!");if(t.query){if(67108864&t.flags&&(!n||0==(16384&n.flags)))throw new Error("Illegal State: Content Query nodes need to be children of directives, at index "+t.nodeIndex+"!");if(134217728&t.flags&&n)throw new Error("Illegal State: View Query nodes have to be top level nodes, at index "+t.nodeIndex+"!")}if(t.childCount){var o=n?n.nodeIndex+n.childCount:e-1;if(t.nodeIndex<=o&&t.nodeIndex+t.childCount>o)throw new Error("Illegal State: childCount of node leads outside of parent, at index "+t.nodeIndex+"!")}}function Cs(n,t,e,r){var o=Os(n.root,n.renderer,n,t,e);return ks(o,n.component,r),Ss(o),o}function Es(n,t,e){var r=Os(n,n.renderer,null,null,t);return ks(r,e,e),Ss(r),r}function xs(n,t,e,r){var o,i=t.element.componentRendererType;return o=i?n.root.rendererFactory.createRenderer(r,i):n.root.renderer,Os(n.root,o,n,t.element.componentProvider,e)}function Os(n,t,e,r,o){var i=new Array(o.nodes.length),l=o.outputCount?new Array(o.outputCount):null;return{def:o,parent:e,viewContainerParent:null,parentNodeDef:r,context:null,component:null,nodes:i,state:13,root:n,renderer:t,oldValues:new Array(o.bindingCount),disposables:l,initIndex:-1}}function ks(n,t,e){n.component=t,n.context=e}function Ss(n){var t;Bl(n)&&(t=ml(n.parent,n.parentNodeDef.parent.nodeIndex).renderElement);for(var e=n.def,r=n.nodes,o=0;o0&&cu(n,t,0,e)&&(p=!0),h>1&&cu(n,t,1,r)&&(p=!0),h>2&&cu(n,t,2,o)&&(p=!0),h>3&&cu(n,t,3,i)&&(p=!0),h>4&&cu(n,t,4,l)&&(p=!0),h>5&&cu(n,t,5,u)&&(p=!0),h>6&&cu(n,t,6,s)&&(p=!0),h>7&&cu(n,t,7,a)&&(p=!0),h>8&&cu(n,t,8,c)&&(p=!0),h>9&&cu(n,t,9,f)&&(p=!0),p}(n,t,e,r,o,i,l,u,s,a,c,f);case 2:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=!1,p=t.bindings,d=p.length;if(d>0&&Ml(n,t,0,e)&&(h=!0),d>1&&Ml(n,t,1,r)&&(h=!0),d>2&&Ml(n,t,2,o)&&(h=!0),d>3&&Ml(n,t,3,i)&&(h=!0),d>4&&Ml(n,t,4,l)&&(h=!0),d>5&&Ml(n,t,5,u)&&(h=!0),d>6&&Ml(n,t,6,s)&&(h=!0),d>7&&Ml(n,t,7,a)&&(h=!0),d>8&&Ml(n,t,8,c)&&(h=!0),d>9&&Ml(n,t,9,f)&&(h=!0),h){var g=t.text.prefix;d>0&&(g+=ms(e,p[0])),d>1&&(g+=ms(r,p[1])),d>2&&(g+=ms(o,p[2])),d>3&&(g+=ms(i,p[3])),d>4&&(g+=ms(l,p[4])),d>5&&(g+=ms(u,p[5])),d>6&&(g+=ms(s,p[6])),d>7&&(g+=ms(a,p[7])),d>8&&(g+=ms(c,p[8])),d>9&&(g+=ms(f,p[9]));var v=yl(n,t.nodeIndex).renderText;n.renderer.setValue(v,g)}return h}(n,t,e,r,o,i,l,u,s,a,c,f);case 16384:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=bl(n,t.nodeIndex),p=h.instance,d=!1,g=void 0,v=t.bindings.length;return v>0&&Dl(n,t,0,e)&&(d=!0,g=ls(n,h,t,0,e,g)),v>1&&Dl(n,t,1,r)&&(d=!0,g=ls(n,h,t,1,r,g)),v>2&&Dl(n,t,2,o)&&(d=!0,g=ls(n,h,t,2,o,g)),v>3&&Dl(n,t,3,i)&&(d=!0,g=ls(n,h,t,3,i,g)),v>4&&Dl(n,t,4,l)&&(d=!0,g=ls(n,h,t,4,l,g)),v>5&&Dl(n,t,5,u)&&(d=!0,g=ls(n,h,t,5,u,g)),v>6&&Dl(n,t,6,s)&&(d=!0,g=ls(n,h,t,6,s,g)),v>7&&Dl(n,t,7,a)&&(d=!0,g=ls(n,h,t,7,a,g)),v>8&&Dl(n,t,8,c)&&(d=!0,g=ls(n,h,t,8,c,g)),v>9&&Dl(n,t,9,f)&&(d=!0,g=ls(n,h,t,9,f,g)),g&&p.ngOnChanges(g),65536&t.flags&&vl(n,256,t.nodeIndex)&&p.ngOnInit(),262144&t.flags&&p.ngDoCheck(),d}(n,t,e,r,o,i,l,u,s,a,c,f);case 32:case 64:case 128:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=t.bindings,p=!1,d=h.length;if(d>0&&Ml(n,t,0,e)&&(p=!0),d>1&&Ml(n,t,1,r)&&(p=!0),d>2&&Ml(n,t,2,o)&&(p=!0),d>3&&Ml(n,t,3,i)&&(p=!0),d>4&&Ml(n,t,4,l)&&(p=!0),d>5&&Ml(n,t,5,u)&&(p=!0),d>6&&Ml(n,t,6,s)&&(p=!0),d>7&&Ml(n,t,7,a)&&(p=!0),d>8&&Ml(n,t,8,c)&&(p=!0),d>9&&Ml(n,t,9,f)&&(p=!0),p){var g=_l(n,t.nodeIndex),v=void 0;switch(201347067&t.flags){case 32:v=new Array(h.length),d>0&&(v[0]=e),d>1&&(v[1]=r),d>2&&(v[2]=o),d>3&&(v[3]=i),d>4&&(v[4]=l),d>5&&(v[5]=u),d>6&&(v[6]=s),d>7&&(v[7]=a),d>8&&(v[8]=c),d>9&&(v[9]=f);break;case 64:v={},d>0&&(v[h[0].name]=e),d>1&&(v[h[1].name]=r),d>2&&(v[h[2].name]=o),d>3&&(v[h[3].name]=i),d>4&&(v[h[4].name]=l),d>5&&(v[h[5].name]=u),d>6&&(v[h[6].name]=s),d>7&&(v[h[7].name]=a),d>8&&(v[h[8].name]=c),d>9&&(v[h[9].name]=f);break;case 128:var y=e;switch(d){case 1:v=y.transform(e);break;case 2:v=y.transform(r);break;case 3:v=y.transform(r,o);break;case 4:v=y.transform(r,o,i);break;case 5:v=y.transform(r,o,i,l);break;case 6:v=y.transform(r,o,i,l,u);break;case 7:v=y.transform(r,o,i,l,u,s);break;case 8:v=y.transform(r,o,i,l,u,s,a);break;case 9:v=y.transform(r,o,i,l,u,s,a,c);break;case 10:v=y.transform(r,o,i,l,u,s,a,c,f)}}g.value=v}return p}(n,t,e,r,o,i,l,u,s,a,c,f);default:throw"unreachable"}}(n,t,r,o,i,l,u,s,a,f,h,p):function(n,t,e){switch(201347067&t.flags){case 1:return function(n,t,e){for(var r=!1,o=0;o0&&Vl(n,t,0,e),h>1&&Vl(n,t,1,r),h>2&&Vl(n,t,2,o),h>3&&Vl(n,t,3,i),h>4&&Vl(n,t,4,l),h>5&&Vl(n,t,5,u),h>6&&Vl(n,t,6,s),h>7&&Vl(n,t,7,a),h>8&&Vl(n,t,8,c),h>9&&Vl(n,t,9,f)}(n,t,r,o,i,l,u,s,a,c,f,h):function(n,t,e){for(var r=0;r0){var i=new Set(n.modules);Ws.forEach(function(t,r){if(i.has(bn(r).providedIn)){var o={token:r,flags:t.flags|(e?4096:0),deps:Zl(t.deps),value:t.value,index:n.providers.length};n.providers.push(o),n.providersByKey[Al(r)]=o}})}}(n=n.factory(function(){return kl})),n):n}(r))}var Qs=new Map,Ws=new Map,Ks=new Map;function Js(n){var t;Qs.set(n.token,n),"function"==typeof n.token&&(t=bn(n.token))&&"function"==typeof t.providedIn&&Ws.set(n.token,n)}function Ys(n,t){var e=Ql(t.viewDefFactory),r=Ql(e.nodes[0].element.componentView);Ks.set(n,r)}function Xs(){Qs.clear(),Ws.clear(),Ks.clear()}function na(n){if(0===Qs.size)return n;var t=function(n){for(var t=[],e=null,r=0;r=this.currentHistoricCoverage.lcq)return!1}else if("branchCoverageIncreaseOnly"===t){var r=this.branchCoverage;if(isNaN(r)||r<=this.currentHistoricCoverage.bcq)return!1}else if("branchCoverageDecreaseOnly"===t&&(r=this.branchCoverage,isNaN(r)||r>=this.currentHistoricCoverage.bcq))return!1;return!0},t.prototype.updateCurrentHistoricCoverage=function(n){if(this.currentHistoricCoverage=null,""!==n)for(var t=0;t-1&&null===e,r}return o(t,n),t.prototype.visible=function(n,t){if(""!==n&&this.name.toLowerCase().indexOf(n.toLowerCase())>-1)return!0;for(var e=0;et&&(r[o].collapsed=n.settings.collapseStates[t]),t++,e(r[o].subElements)};e(this.codeElements)},n}(),La=new I(function(n){return n.complete()}),za=function(n){function t(t,e){var r=n.call(this,t)||this;r.sources=e,r.completed=0,r.haveValues=0;var o=e.length;r.values=new Array(o);for(var i=0;i0},t.prototype.tagName=function(n){return n.tagName},t.prototype.attributeMap=function(n){for(var t=new Map,e=n.attributes,r=0;r0;l||(l=n[i]=[]);var s=$c(t)?Zone.root:Zone.current;if(0===l.length)l.push({zone:s,handler:o});else{for(var a=!1,c=0;c-1},t}(kc),tf=["alt","control","meta","shift"],ef={alt:function(n){return n.altKey},control:function(n){return n.ctrlKey},meta:function(n){return n.metaKey},shift:function(n){return n.shiftKey}},rf=function(n){function t(t){return n.call(this,t)||this}var e;return o(t,n),e=t,t.prototype.supports=function(n){return null!=e.parseEventName(n)},t.prototype.addEventListener=function(n,t,r){var o=e.parseEventName(t),i=e.eventCallback(o.fullKey,r,this.manager.getZone());return this.manager.getZone().runOutsideAngular(function(){return uc().onAndCancel(n,o.domEventName,i)})},t.parseEventName=function(n){var t=n.toLowerCase().split("."),r=t.shift();if(0===t.length||"keydown"!==r&&"keyup"!==r)return null;var o=e._normalizeKey(t.pop()),i="";if(tf.forEach(function(n){var e=t.indexOf(n);e>-1&&(t.splice(e,1),i+=n+".")}),i+=o,0!=t.length||0===o.length)return null;var l={};return l.domEventName=r,l.fullKey=i,l},t.getEventFullKey=function(n){var t="",e=uc().getEventKey(n);return" "===(e=e.toLowerCase())?e="space":"."===e&&(e="dot"),tf.forEach(function(r){r!=e&&(0,ef[r])(n)&&(t+=r+".")}),t+=e},t.eventCallback=function(n,t,r){return function(o){e.getEventFullKey(o)===n&&r.runGuarded(function(){return t(o)})}},t._normalizeKey=function(n){switch(n){case"esc":return"escape";default:return n}},t}(kc),of=function(){return function(){}}(),lf=function(n){function t(t){var e=n.call(this)||this;return e._doc=t,e}return o(t,n),t.prototype.sanitize=function(n,t){if(null==t)return null;switch(n){case Lo.NONE:return t;case Lo.HTML:return t instanceof sf?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"HTML"),function(n,t){var e=null;try{_o=_o||new po(n);var r=t?String(t):"";e=_o.getInertBodyElement(r);var o=5,i=r;do{if(0===o)throw new Error("Failed to sanitize html because the input is unstable");o--,r=i,i=e.innerHTML,e=_o.getInertBodyElement(r)}while(r!==i);var l=new To,u=l.sanitizeChildren(Do(e)||e);return ho()&&l.sanitizedSomething&&console.warn("WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."),u}finally{if(e)for(var s=Do(e)||e;s.firstChild;)s.removeChild(s.firstChild)}}(this._doc,String(t)));case Lo.STYLE:return t instanceof af?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"Style"),function(n){if(!(n=String(n).trim()))return"";var t=n.match(Bo);return t&&yo(t[1])===t[1]||n.match(Fo)&&function(n){for(var t=!0,e=!0,r=0;rn?{max:{max:n,actual:t.value}}:null}},n.required=function(n){return mf(n.value)?{required:!0}:null},n.requiredTrue=function(n){return!0===n.value?null:{required:!0}},n.email=function(n){return mf(n.value)?null:bf.test(n.value)?null:{email:!0}},n.minLength=function(n){return function(t){if(mf(t.value))return null;var e=t.value?t.value.length:0;return en?{maxlength:{requiredLength:n,actualLength:e}}:null}},n.pattern=function(t){return t?("string"==typeof t?(r="","^"!==t.charAt(0)&&(r+="^"),r+=t,"$"!==t.charAt(t.length-1)&&(r+="$"),e=new RegExp(r)):(r=t.toString(),e=t),function(n){if(mf(n.value))return null;var t=n.value;return e.test(t)?null:{pattern:{requiredPattern:r,actualValue:t}}}):n.nullValidator;var e,r},n.nullValidator=function(n){return null},n.compose=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return Ef(function(n,e){return t.map(function(t){return t(n)})}(n))}},n.composeAsync=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return function n(){for(var t,e=[],r=0;r=0;--t)if(this._accessors[t][1]===n)return void this._accessors.splice(t,1)},n.prototype.select=function(n){var t=this;this._accessors.forEach(function(e){t._isSameGroup(e,n)&&e[1]!==n&&e[1].fireUncheck(n.value)})},n.prototype._isSameGroup=function(n,t){return!!n[0].control&&n[0]._parent===t._control._parent&&n[1].name===t.name},n}(),Mf=function(){function n(n,t,e,r){this._renderer=n,this._elementRef=t,this._registry=e,this._injector=r,this.onChange=function(){},this.onTouched=function(){}}return n.prototype.ngOnInit=function(){this._control=this._injector.get(Nf),this._checkName(),this._registry.add(this._control,this)},n.prototype.ngOnDestroy=function(){this._registry.remove(this)},n.prototype.writeValue=function(n){this._state=n===this.value,this._renderer.setProperty(this._elementRef.nativeElement,"checked",this._state)},n.prototype.registerOnChange=function(n){var t=this;this._fn=n,this.onChange=function(){n(t.value),t._registry.select(t)}},n.prototype.fireUncheck=function(n){this.writeValue(n)},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._checkName=function(){this.name&&this.formControlName&&this.name!==this.formControlName&&this._throwNameError(),!this.name&&this.formControlName&&(this.name=this.formControlName)},n.prototype._throwNameError=function(){throw new Error('\n If you define both a name and a formControlName attribute on your radio button, their values\n must match. Ex: \n ')},n}(),Vf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this.onChange=function(n){},this.onTouched=function(){}}return n.prototype.writeValue=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"value",parseFloat(n))},n.prototype.registerOnChange=function(n){this.onChange=function(t){n(""==t?null:parseFloat(t))}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n}(),Hf='\n

\n
\n \n
\n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n person: new FormGroup({ firstName: new FormControl() })\n });',Rf='\n
\n
\n \n
\n
';function jf(n,t){return null==n?""+t:(t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Lf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){this.value=n;var t=this._getOptionId(n);null==t&&this._renderer.setProperty(this._elementRef.nativeElement,"selectedIndex",-1);var e=jf(t,n);this._renderer.setProperty(this._elementRef.nativeElement,"value",e)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){t.value=t._getOptionValue(e),n(t.value)}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._registerOption=function(){return(this._idCounter++).toString()},n.prototype._getOptionId=function(n){var t,e;try{for(var r=s(Array.from(this._optionMap.keys())),o=r.next();!o.done;o=r.next()){var i=o.value;if(this._compareWith(this._optionMap.get(i),n))return i}}catch(l){t={error:l}}finally{try{o&&!o.done&&(e=r.return)&&e.call(r)}finally{if(t)throw t.error}}return null},n.prototype._getOptionValue=function(n){var t=function(n){return n.split(":")[0]}(n);return this._optionMap.has(t)?this._optionMap.get(t):n},n}(),zf=function(){function n(n,t,e){this._element=n,this._renderer=t,this._select=e,this._select&&(this.id=this._select._registerOption())}return Object.defineProperty(n.prototype,"ngValue",{set:function(n){null!=this._select&&(this._select._optionMap.set(this.id,n),this._setElementValue(jf(this.id,n)),this._select.writeValue(this._select.value))},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"value",{set:function(n){this._setElementValue(n),this._select&&this._select.writeValue(this._select.value)},enumerable:!0,configurable:!0}),n.prototype._setElementValue=function(n){this._renderer.setProperty(this._element.nativeElement,"value",n)},n.prototype.ngOnDestroy=function(){this._select&&(this._select._optionMap.delete(this.id),this._select.writeValue(this._select.value))},n}();function Ff(n,t){return null==n?""+t:("string"==typeof t&&(t="'"+t+"'"),t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Bf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){var t,e=this;if(this.value=n,Array.isArray(n)){var r=n.map(function(n){return e._getOptionId(n)});t=function(n,t){n._setSelected(r.indexOf(t.toString())>-1)}}else t=function(n,t){n._setSelected(!1)};this._optionMap.forEach(t)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){var r=[];if(e.hasOwnProperty("selectedOptions"))for(var o=e.selectedOptions,i=0;i1?"path: '"+n.path.join(" -> ")+"'":n.path[0]?"name: '"+n.path+"'":"unspecified name attribute",new Error(t+" "+e)}function Qf(n){return null!=n?_f.compose(n.map(Af)):null}function Wf(n){return null!=n?_f.composeAsync(n.map(Tf)):null}var Kf=[Of,Vf,If,Lf,Bf,Mf],Jf=function(n){function t(){return null!==n&&n.apply(this,arguments)||this}return o(t,n),t.prototype.ngOnInit=function(){this._checkParentType(),this.formDirective.addFormGroup(this)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeFormGroup(this)},Object.defineProperty(t.prototype,"control",{get:function(){return this.formDirective.getFormGroup(this)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return Gf(this.name,this._parent)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._validators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._asyncValidators)},enumerable:!0,configurable:!0}),t.prototype._checkParentType=function(){},t}(yf),Yf=function(n){function t(t){return n.call(this,t)||this}return o(t,n),t}(function(){function n(n){this._cd=n}return Object.defineProperty(n.prototype,"ngClassUntouched",{get:function(){return!!this._cd.control&&this._cd.control.untouched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassTouched",{get:function(){return!!this._cd.control&&this._cd.control.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPristine",{get:function(){return!!this._cd.control&&this._cd.control.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassDirty",{get:function(){return!!this._cd.control&&this._cd.control.dirty},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassValid",{get:function(){return!!this._cd.control&&this._cd.control.valid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassInvalid",{get:function(){return!!this._cd.control&&this._cd.control.invalid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPending",{get:function(){return!!this._cd.control&&this._cd.control.pending},enumerable:!0,configurable:!0}),n}());function Xf(n){var t=th(n)?n.validators:n;return Array.isArray(t)?Qf(t):t||null}function nh(n,t){var e=th(t)?t.asyncValidators:n;return Array.isArray(e)?Wf(e):e||null}function th(n){return null!=n&&!Array.isArray(n)&&"object"==typeof n}var eh=function(){function n(n,t){this.validator=n,this.asyncValidator=t,this._onCollectionChange=function(){},this.pristine=!0,this.touched=!1,this._onDisabledChange=[]}return Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"valid",{get:function(){return"VALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"invalid",{get:function(){return"INVALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"pending",{get:function(){return"PENDING"==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"disabled",{get:function(){return"DISABLED"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"enabled",{get:function(){return"DISABLED"!==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"dirty",{get:function(){return!this.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"untouched",{get:function(){return!this.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"updateOn",{get:function(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"},enumerable:!0,configurable:!0}),n.prototype.setValidators=function(n){this.validator=Xf(n)},n.prototype.setAsyncValidators=function(n){this.asyncValidator=nh(n)},n.prototype.clearValidators=function(){this.validator=null},n.prototype.clearAsyncValidators=function(){this.asyncValidator=null},n.prototype.markAsTouched=function(n){void 0===n&&(n={}),this.touched=!0,this._parent&&!n.onlySelf&&this._parent.markAsTouched(n)},n.prototype.markAsUntouched=function(n){void 0===n&&(n={}),this.touched=!1,this._pendingTouched=!1,this._forEachChild(function(n){n.markAsUntouched({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype.markAsDirty=function(n){void 0===n&&(n={}),this.pristine=!1,this._parent&&!n.onlySelf&&this._parent.markAsDirty(n)},n.prototype.markAsPristine=function(n){void 0===n&&(n={}),this.pristine=!0,this._pendingDirty=!1,this._forEachChild(function(n){n.markAsPristine({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype.markAsPending=function(n){void 0===n&&(n={}),this.status="PENDING",!1!==n.emitEvent&&this.statusChanges.emit(this.status),this._parent&&!n.onlySelf&&this._parent.markAsPending(n)},n.prototype.disable=function(n){void 0===n&&(n={}),this.status="DISABLED",this.errors=null,this._forEachChild(function(t){t.disable(i({},n,{onlySelf:!0}))}),this._updateValue(),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!0)})},n.prototype.enable=function(n){void 0===n&&(n={}),this.status="VALID",this._forEachChild(function(t){t.enable(i({},n,{onlySelf:!0}))}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!1)})},n.prototype._updateAncestors=function(n){this._parent&&!n.onlySelf&&(this._parent.updateValueAndValidity(n),this._parent._updatePristine(),this._parent._updateTouched())},n.prototype.setParent=function(n){this._parent=n},n.prototype.updateValueAndValidity=function(n){void 0===n&&(n={}),this._setInitialStatus(),this._updateValue(),this.enabled&&(this._cancelExistingSubscription(),this.errors=this._runValidator(),this.status=this._calculateStatus(),"VALID"!==this.status&&"PENDING"!==this.status||this._runAsyncValidator(n.emitEvent)),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._parent&&!n.onlySelf&&this._parent.updateValueAndValidity(n)},n.prototype._updateTreeValidity=function(n){void 0===n&&(n={emitEvent:!0}),this._forEachChild(function(t){return t._updateTreeValidity(n)}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})},n.prototype._setInitialStatus=function(){this.status=this._allControlsDisabled()?"DISABLED":"VALID"},n.prototype._runValidator=function(){return this.validator?this.validator(this):null},n.prototype._runAsyncValidator=function(n){var t=this;if(this.asyncValidator){this.status="PENDING";var e=Cf(this.asyncValidator(this));this._asyncValidationSubscription=e.subscribe(function(e){return t.setErrors(e,{emitEvent:n})})}},n.prototype._cancelExistingSubscription=function(){this._asyncValidationSubscription&&this._asyncValidationSubscription.unsubscribe()},n.prototype.setErrors=function(n,t){void 0===t&&(t={}),this.errors=n,this._updateControlsErrors(!1!==t.emitEvent)},n.prototype.get=function(n){return function(n,t,e){return null==t?null:(t instanceof Array||(t=t.split(".")),t instanceof Array&&0===t.length?null:t.reduce(function(n,t){return n instanceof oh?n.controls.hasOwnProperty(t)?n.controls[t]:null:n instanceof ih&&n.at(t)||null},n))}(this,n)},n.prototype.getError=function(n,t){var e=t?this.get(t):this;return e&&e.errors?e.errors[n]:null},n.prototype.hasError=function(n,t){return!!this.getError(n,t)},Object.defineProperty(n.prototype,"root",{get:function(){for(var n=this;n._parent;)n=n._parent;return n},enumerable:!0,configurable:!0}),n.prototype._updateControlsErrors=function(n){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),this._parent&&this._parent._updateControlsErrors(n)},n.prototype._initObservables=function(){this.valueChanges=new Ho,this.statusChanges=new Ho},n.prototype._calculateStatus=function(){return this._allControlsDisabled()?"DISABLED":this.errors?"INVALID":this._anyControlsHaveStatus("PENDING")?"PENDING":this._anyControlsHaveStatus("INVALID")?"INVALID":"VALID"},n.prototype._anyControlsHaveStatus=function(n){return this._anyControls(function(t){return t.status===n})},n.prototype._anyControlsDirty=function(){return this._anyControls(function(n){return n.dirty})},n.prototype._anyControlsTouched=function(){return this._anyControls(function(n){return n.touched})},n.prototype._updatePristine=function(n){void 0===n&&(n={}),this.pristine=!this._anyControlsDirty(),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype._updateTouched=function(n){void 0===n&&(n={}),this.touched=this._anyControlsTouched(),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype._isBoxedValue=function(n){return"object"==typeof n&&null!==n&&2===Object.keys(n).length&&"value"in n&&"disabled"in n},n.prototype._registerOnCollectionChange=function(n){this._onCollectionChange=n},n.prototype._setUpdateStrategy=function(n){th(n)&&null!=n.updateOn&&(this._updateOn=n.updateOn)},n}(),rh=function(n){function t(t,e,r){void 0===t&&(t=null);var o=n.call(this,Xf(e),nh(r,e))||this;return o._onChange=[],o._applyFormState(t),o._setUpdateStrategy(e),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o._initObservables(),o}return o(t,n),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this.value=this._pendingValue=n,this._onChange.length&&!1!==t.emitModelToViewChange&&this._onChange.forEach(function(n){return n(e.value,!1!==t.emitViewToModelChange)}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){void 0===t&&(t={}),this.setValue(n,t)},t.prototype.reset=function(n,t){void 0===n&&(n=null),void 0===t&&(t={}),this._applyFormState(n),this.markAsPristine(t),this.markAsUntouched(t),this.setValue(this.value,t),this._pendingChange=!1},t.prototype._updateValue=function(){},t.prototype._anyControls=function(n){return!1},t.prototype._allControlsDisabled=function(){return this.disabled},t.prototype.registerOnChange=function(n){this._onChange.push(n)},t.prototype._clearChangeFns=function(){this._onChange=[],this._onDisabledChange=[],this._onCollectionChange=function(){}},t.prototype.registerOnDisabledChange=function(n){this._onDisabledChange.push(n)},t.prototype._forEachChild=function(n){},t.prototype._syncPendingControls=function(){return!("submit"!==this.updateOn||(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),!this._pendingChange)||(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),0))},t.prototype._applyFormState=function(n){this._isBoxedValue(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n},t}(eh),oh=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.registerControl=function(n,t){return this.controls[n]?this.controls[n]:(this.controls[n]=t,t.setParent(this),t._registerOnCollectionChange(this._onCollectionChange),t)},t.prototype.addControl=function(n,t){this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.removeControl=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],t&&this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.contains=function(n){return this.controls.hasOwnProperty(n)&&this.controls[n].enabled},t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),Object.keys(n).forEach(function(r){e._throwIfControlMissing(r),e.controls[r].setValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),Object.keys(n).forEach(function(r){e.controls[r]&&e.controls[r].patchValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n={}),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this._reduceChildren({},function(n,t,e){return n[e]=t instanceof rh?t.value:t.getRawValue(),n})},t.prototype._syncPendingControls=function(){var n=this._reduceChildren(!1,function(n,t){return!!t._syncPendingControls()||n});return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!Object.keys(this.controls).length)throw new Error("\n There are no form controls registered with this group yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.controls[n])throw new Error("Cannot find form control with name: "+n+".")},t.prototype._forEachChild=function(n){var t=this;Object.keys(this.controls).forEach(function(e){return n(t.controls[e],e)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){t.setParent(n),t._registerOnCollectionChange(n._onCollectionChange)})},t.prototype._updateValue=function(){this.value=this._reduceValue()},t.prototype._anyControls=function(n){var t=this,e=!1;return this._forEachChild(function(r,o){e=e||t.contains(o)&&n(r)}),e},t.prototype._reduceValue=function(){var n=this;return this._reduceChildren({},function(t,e,r){return(e.enabled||n.disabled)&&(t[r]=e.value),t})},t.prototype._reduceChildren=function(n,t){var e=n;return this._forEachChild(function(n,r){e=t(e,n,r)}),e},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(Object.keys(this.controls)),r=e.next();!r.done;r=e.next())if(this.controls[r.value].enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return Object.keys(this.controls).length>0||this.disabled},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control with name: '"+e+"'.")})},t}(eh),ih=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.at=function(n){return this.controls[n]},t.prototype.push=function(n){this.controls.push(n),this._registerControl(n),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.insert=function(n,t){this.controls.splice(n,0,t),this._registerControl(t),this.updateValueAndValidity()},t.prototype.removeAt=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),this.updateValueAndValidity()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),t&&(this.controls.splice(n,0,t),this._registerControl(t)),this.updateValueAndValidity(),this._onCollectionChange()},Object.defineProperty(t.prototype,"length",{get:function(){return this.controls.length},enumerable:!0,configurable:!0}),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),n.forEach(function(n,r){e._throwIfControlMissing(r),e.at(r).setValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),n.forEach(function(n,r){e.at(r)&&e.at(r).patchValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n=[]),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this.controls.map(function(n){return n instanceof rh?n.value:n.getRawValue()})},t.prototype._syncPendingControls=function(){var n=this.controls.reduce(function(n,t){return!!t._syncPendingControls()||n},!1);return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!this.controls.length)throw new Error("\n There are no form controls registered with this array yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.at(n))throw new Error("Cannot find form control at index "+n)},t.prototype._forEachChild=function(n){this.controls.forEach(function(t,e){n(t,e)})},t.prototype._updateValue=function(){var n=this;this.value=this.controls.filter(function(t){return t.enabled||n.disabled}).map(function(n){return n.value})},t.prototype._anyControls=function(n){return this.controls.some(function(t){return t.enabled&&n(t)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){return n._registerControl(t)})},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control at index: "+e+".")})},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(this.controls),r=e.next();!r.done;r=e.next())if(r.value.enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return this.controls.length>0||this.disabled},t.prototype._registerControl=function(n){n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)},t}(eh),lh=Promise.resolve(null),uh=function(n){function t(t,e){var r=n.call(this)||this;return r.submitted=!1,r._directives=[],r.ngSubmit=new Ho,r.form=new oh({},Qf(t),Wf(e)),r}return o(t,n),t.prototype.ngAfterViewInit=function(){this._setUpdateStrategy()},Object.defineProperty(t.prototype,"formDirective",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"control",{get:function(){return this.form},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return[]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"controls",{get:function(){return this.form.controls},enumerable:!0,configurable:!0}),t.prototype.addControl=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);n.control=e.registerControl(n.name,n.control),Zf(n.control,n),n.control.updateValueAndValidity({emitEvent:!1}),t._directives.push(n)})},t.prototype.getControl=function(n){return this.form.get(n.path)},t.prototype.removeControl=function(n){var t=this;lh.then(function(){var e,r,o=t._findContainer(n.path);o&&o.removeControl(n.name),(r=(e=t._directives).indexOf(n))>-1&&e.splice(r,1)})},t.prototype.addFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path),r=new oh({});(function(n,t){null==n&&$f(t,"Cannot find control with"),n.validator=_f.compose([n.validator,t.validator]),n.asyncValidator=_f.composeAsync([n.asyncValidator,t.asyncValidator])})(r,n),e.registerControl(n.name,r),r.updateValueAndValidity({emitEvent:!1})})},t.prototype.removeFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);e&&e.removeControl(n.name)})},t.prototype.getFormGroup=function(n){return this.form.get(n.path)},t.prototype.updateModel=function(n,t){var e=this;lh.then(function(){e.form.get(n.path).setValue(t)})},t.prototype.setValue=function(n){this.control.setValue(n)},t.prototype.onSubmit=function(n){return this.submitted=!0,t=this._directives,this.form._syncPendingControls(),t.forEach(function(n){var t=n.control;"submit"===t.updateOn&&t._pendingChange&&(n.viewToModelUpdate(t._pendingValue),t._pendingChange=!1)}),this.ngSubmit.emit(n),!1;var t},t.prototype.onReset=function(){this.resetForm()},t.prototype.resetForm=function(n){void 0===n&&(n=void 0),this.form.reset(n),this.submitted=!1},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.form._updateOn=this.options.updateOn)},t.prototype._findContainer=function(n){return n.pop(),n.length?this.form.get(n):this.form},t}(yf),sh=function(){function n(){}return n.modelParentException=function(){throw new Error('\n ngModel cannot be used to register form controls with a parent formGroup directive. Try using\n formGroup\'s partner directive "formControlName" instead. Example:\n\n \n
\n \n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n firstName: new FormControl()\n });\n\n Or, if you\'d like to avoid registering this form control, indicate that it\'s standalone in ngModelOptions:\n\n Example:\n\n \n
\n \n \n
\n ')},n.formGroupNameException=function(){throw new Error("\n ngModel cannot be used to register form controls with a parent formGroupName or formArrayName directive.\n\n Option 1: Use formControlName instead of ngModel (reactive strategy):\n\n "+Hf+"\n\n Option 2: Update ngModel's parent be ngModelGroup (template-driven strategy):\n\n "+Rf)},n.missingNameException=function(){throw new Error('If ngModel is used within a form tag, either the name attribute must be set or the form\n control must be defined as \'standalone\' in ngModelOptions.\n\n Example 1: \n Example 2: ')},n.modelGroupParentException=function(){throw new Error("\n ngModelGroup cannot be used with a parent formGroup directive.\n\n Option 1: Use formGroupName instead of ngModelGroup (reactive strategy):\n\n "+Hf+"\n\n Option 2: Use a regular form tag instead of the formGroup directive (template-driven strategy):\n\n "+Rf)},n.ngFormWarning=function(){console.warn("\n It looks like you're using 'ngForm'.\n\n Support for using the 'ngForm' element selector has been deprecated in Angular v6 and will be removed\n in Angular v9.\n\n Use 'ng-form' instead.\n\n Before:\n \n\n After:\n \n ")},n}(),ah=new wn("NgFormSelectorWarning"),ch=function(n){function t(t,e,r){var o=n.call(this)||this;return o._parent=t,o._validators=e,o._asyncValidators=r,o}var e;return o(t,n),e=t,t.prototype._checkParentType=function(){this._parent instanceof e||this._parent instanceof uh||sh.modelGroupParentException()},t}(Jf),fh=Promise.resolve(null),hh=function(n){function t(t,e,r,o){var i=n.call(this)||this;return i.control=new rh,i._registered=!1,i.update=new Ho,i._parent=t,i._rawValidators=e||[],i._rawAsyncValidators=r||[],i.valueAccessor=function(n,t){if(!t)return null;Array.isArray(t)||$f(n,"Value accessor was not provided as an array for form control with");var e=void 0,r=void 0,o=void 0;return t.forEach(function(t){var i;t.constructor===Sf?e=t:(i=t,Kf.some(function(n){return i.constructor===n})?(r&&$f(n,"More than one built-in value accessor matches form control with"),r=t):(o&&$f(n,"More than one custom value accessor matches form control with"),o=t))}),o||r||e||($f(n,"No valid value accessor for form control with"),null)}(i,o),i}return o(t,n),t.prototype.ngOnChanges=function(n){this._checkForErrors(),this._registered||this._setUpControl(),"isDisabled"in n&&this._updateDisabled(n),function(n,t){if(!n.hasOwnProperty("model"))return!1;var e=n.model;return!!e.isFirstChange()||!Nn(t,e.currentValue)}(n,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeControl(this)},Object.defineProperty(t.prototype,"path",{get:function(){return this._parent?Gf(this.name,this._parent):[this.name]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._rawValidators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._rawAsyncValidators)},enumerable:!0,configurable:!0}),t.prototype.viewToModelUpdate=function(n){this.viewModel=n,this.update.emit(n)},t.prototype._setUpControl=function(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.control._updateOn=this.options.updateOn)},t.prototype._isStandalone=function(){return!this._parent||!(!this.options||!this.options.standalone)},t.prototype._setUpStandalone=function(){Zf(this.control,this),this.control.updateValueAndValidity({emitEvent:!1})},t.prototype._checkForErrors=function(){this._isStandalone()||this._checkParentType(),this._checkName()},t.prototype._checkParentType=function(){!(this._parent instanceof ch)&&this._parent instanceof Jf?sh.formGroupNameException():this._parent instanceof ch||this._parent instanceof uh||sh.modelParentException()},t.prototype._checkName=function(){this.options&&this.options.name&&(this.name=this.options.name),this._isStandalone()||this.name||sh.missingNameException()},t.prototype._updateValue=function(n){var t=this;fh.then(function(){t.control.setValue(n,{emitViewToModelChange:!1})})},t.prototype._updateDisabled=function(n){var t=this,e=n.isDisabled.currentValue,r=""===e||e&&"false"!==e;fh.then(function(){r&&!t.control.disabled?t.control.disable():!r&&t.control.disabled&&t.control.enable()})},t}(Nf),ph=function(){return function(){}}(),dh=function(){function n(){}var t;return t=n,n.withConfig=function(n){return{ngModule:t,providers:[{provide:ah,useValue:n.warnOnDeprecatedNgFormSelector}]}},n}(),gh=Pl({encapsulation:2,styles:[],data:{}});function vh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function yh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","20"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["20"]))],function(n,t){n(t,1,0,"20"),n(t,2,0,"20")},null)}function mh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","50"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["50"]))],function(n,t){n(t,1,0,"50"),n(t,2,0,"50")},null)}function bh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","100"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["100"]))],function(n,t){n(t,1,0,"100"),n(t,2,0,"100")},null)}function _h(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){var e=t.component;n(t,1,0,e.totalNumberOfRiskHotspots),n(t,2,0,e.totalNumberOfRiskHotspots)},function(n,t){n(t,3,0,t.component.translations.all)})}function wh(n){return bs(0,[(n()(),lu(0,0,null,null,17,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,1).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,1).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.numberOfRiskHotspots=e)&&r),r},null,null)),Qu(1,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(3,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(5,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(6,0,null,null,3,"option",[["value","10"]],null,null,null,null,null)),Qu(7,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(8,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["10"])),(n()(),iu(16777216,null,null,1,null,yh)),Qu(11,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mh)),Qu(13,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bh)),Qu(15,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,_h)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,3,0,e.settings.numberOfRiskHotspots),n(t,7,0,"10"),n(t,8,0,"10"),n(t,11,0,e.totalNumberOfRiskHotspots>10),n(t,13,0,e.totalNumberOfRiskHotspots>20),n(t,15,0,e.totalNumberOfRiskHotspots>50),n(t,17,0,e.totalNumberOfRiskHotspots>100)},function(n,t){n(t,0,0,Vu(t,5).ngClassUntouched,Vu(t,5).ngClassTouched,Vu(t,5).ngClassPristine,Vu(t,5).ngClassDirty,Vu(t,5).ngClassValid,Vu(t,5).ngClassInvalid,Vu(t,5).ngClassPending)})}function Ch(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null))],null,null)}function Eh(n){return bs(0,[(n()(),lu(0,0,null,null,7,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting(""+n.context.index,e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),lu(7,0,null,null,0,"i",[["class","icon-info-circled"]],null,null,null,null,null))],function(n,t){var e=t.component,r=n(t,4,0,e.settings.sortBy===""+t.context.index&&"desc"===e.settings.sortOrder,e.settings.sortBy===""+t.context.index&&"asc"===e.settings.sortOrder,e.settings.sortBy!==""+t.context.index);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.context.$implicit.name),n(t,6,0,ru(1,"",t.context.$implicit.explanationUrl,""))})}function xh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"td",[["class","right"]],null,null,null,null,null)),Qu(1,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(2,{lightred:0,lightgreen:1}),(n()(),vs(3,null,["",""]))],function(n,t){var e=n(t,2,0,t.context.$implicit.exceeded,!t.context.$implicit.exceeded);n(t,1,0,"right",e)},function(n,t){n(t,3,0,t.context.$implicit.value)})}function Oh(n){return bs(0,[(n()(),lu(0,0,null,null,10,"tr",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"td",[],null,null,null,null,null)),(n()(),vs(2,null,["",""])),(n()(),lu(3,0,null,null,2,"td",[],null,null,null,null,null)),(n()(),lu(4,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,2,"td",[],[[8,"title",0]],null,null,null,null)),(n()(),lu(7,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(8,null,[" "," "])),(n()(),iu(16777216,null,null,1,null,xh)),Qu(10,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){n(t,10,0,t.context.$implicit.metrics)},function(n,t){n(t,2,0,t.context.$implicit.assembly),n(t,4,0,t.context.$implicit.reportPath),n(t,5,0,t.context.$implicit.class),n(t,6,0,t.context.$implicit.methodName),n(t,7,0,t.context.$implicit.reportPath+"#file"+t.context.$implicit.fileIndex+"_line"+t.context.$implicit.line),n(t,8,0,t.context.$implicit.methodShortName)})}function kh(n){return bs(0,[(n()(),lu(0,0,null,null,62,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,28,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,12,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,11,"select",[["name","assembly"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.assembly=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{name:[0,"name"],model:[1,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,vh)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(15,0,null,null,4,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(16,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(17,null,["",""])),(n()(),iu(16777216,null,null,1,null,wh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,0,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(21,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(22,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(23,null,[""," "])),(n()(),lu(24,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,25)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,25).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,25)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,25)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(25,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(27,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(29,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(30,0,null,null,32,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(31,0,null,null,5,"colgroup",[],null,null,null,null,null)),(n()(),lu(32,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(33,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(34,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ch)),Qu(36,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(37,0,null,null,21,"thead",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,20,"tr",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(40,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("assembly",e)&&r),r},null,null)),(n()(),lu(41,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(42,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(43,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(44,null,["",""])),(n()(),lu(45,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(46,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("class",e)&&r),r},null,null)),(n()(),lu(47,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(48,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(49,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(50,null,["",""])),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("method",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),iu(16777216,null,null,1,null,Eh)),Qu(58,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(59,0,null,null,3,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,2,null,Oh)),Qu(61,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(t=0,e=ec,r=[],Ku(-1,t|=16,null,0,e,e,r))],function(n,t){var e=t.component;n(t,6,0,"assembly",e.settings.assembly),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.assemblies),n(t,19,0,e.totalNumberOfRiskHotspots>10),n(t,27,0,e.settings.filter),n(t,36,0,e.riskHotspotMetrics);var r=n(t,43,0,"assembly"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"assembly"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"assembly"!==e.settings.sortBy);n(t,42,0,"icon-down-dir",r);var o=n(t,49,0,"class"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"class"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"class"!==e.settings.sortBy);n(t,48,0,"icon-down-dir",o);var i=n(t,55,0,"method"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"method"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"method"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",i),n(t,58,0,e.riskHotspotMetrics),n(t,61,0,function(n,t,e,r){if(_t.isWrapped(r)){r=_t.unwrap(r);var o=n.def.nodes[61].bindingIndex+0,i=_t.unwrap(n.oldValues[o]);n.oldValues[o]=new _t(i)}return r}(t,0,0,Vu(t,62).transform(e.riskHotspots,0,e.settings.numberOfRiskHotspots)))},function(n,t){var e=t.component;n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.assembly),n(t,17,0,e.translations.top),n(t,23,0,e.translations.filter),n(t,24,0,Vu(t,29).ngClassUntouched,Vu(t,29).ngClassTouched,Vu(t,29).ngClassPristine,Vu(t,29).ngClassDirty,Vu(t,29).ngClassValid,Vu(t,29).ngClassInvalid,Vu(t,29).ngClassPending),n(t,44,0,e.translations.assembly),n(t,50,0,e.translations.class),n(t,56,0,e.translations.method)});var t,e,r}function Sh(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,kh)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.totalNumberOfRiskHotspots>0)},null)}function Ah(n){return bs(0,[(n()(),lu(0,0,null,null,1,"risk-hotspots",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Sh,gh)),Qu(1,114688,null,0,Na,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Th=xu("risk-hotspots",Na,Ah,{},{},[]),Ih=function(){function n(){this.grayVisible=!0,this.greenVisible=!1,this.redVisible=!1,this.greenClass="",this.redClass="",this._percentage=NaN}return Object.defineProperty(n.prototype,"percentage",{get:function(){return this._percentage},set:function(n){this._percentage=n,this.grayVisible=isNaN(n),this.greenVisible=!isNaN(n)&&Math.round(n)>0,this.redVisible=!isNaN(n)&&100-Math.round(n)>0,this.greenClass="covered"+Math.round(n),this.redClass="covered"+(100-Math.round(n))},enumerable:!0,configurable:!0}),n}(),Ph=Pl({encapsulation:2,styles:[],data:{}});function Nh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[["class","gray covered100"]],null,null,null,null,null))],null,null)}function Dh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"green ",t.component.greenClass,""))})}function Mh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"red ",t.component.redClass,""))})}function Vh(n){return bs(2,[(n()(),lu(0,0,null,null,6,"table",[["class","coverage"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Nh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Dh)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Mh)),Qu(6,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,e.grayVisible),n(t,4,0,e.greenVisible),n(t,6,0,e.redVisible)},null)}var Hh=function(){return function(){this.element=null,this.collapsed=!1,this.branchCoverageAvailable=!1}}(),Rh=Pl({encapsulation:2,styles:[],data:{}});function jh(n){return bs(0,[(n()(),lu(0,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.element.branchCoveragePercentage)})}function Lh(n){return bs(0,[(n()(),lu(0,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.element.branchCoverage)},null)}function zh(n){return bs(2,[(n()(),lu(0,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.element.toggleCollapse(e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{ngClass:[0,"ngClass"]},null),gs(4,{"icon-plus":0,"icon-minus":1}),(n()(),vs(5,null,[" ",""])),(n()(),lu(6,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(9,null,["",""])),(n()(),lu(10,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(11,null,["",""])),(n()(),lu(12,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(13,null,["",""])),(n()(),lu(14,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(15,null,["",""])),(n()(),lu(16,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(17,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(18,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,jh)),Qu(20,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Lh)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component,r=n(t,4,0,e.element.collapsed,!e.element.collapsed);n(t,3,0,r),n(t,18,0,e.element.coverage),n(t,20,0,e.branchCoverageAvailable),n(t,22,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,5,0,e.element.name),n(t,7,0,e.element.coveredLines),n(t,9,0,e.element.uncoveredLines),n(t,11,0,e.element.coverableLines),n(t,13,0,e.element.totalLines),n(t,15,0,e.element.coveragePercentage)})}var Fh=function(){function n(){this.path=null,this._historicCoverages=[]}return Object.defineProperty(n.prototype,"historicCoverages",{get:function(){return this._historicCoverages},set:function(n){if(this._historicCoverages=n,n.length>1){for(var t="",e=0;et?"lightgreen":n1),n(t,4,0,null!==e.clazz.currentHistoricCoverage),n(t,6,0,null===e.clazz.currentHistoricCoverage)},null)}function ap(n){return bs(0,[(n()(),lu(0,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.clazz.branchCoverage)},null)}function cp(n){return bs(2,[(n()(),lu(0,0,null,null,4,"td",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,qh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,$h)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(5,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Qh)),Qu(7,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Wh)),Qu(9,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(10,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Kh)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Jh)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Yh)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Xh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,np)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,tp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(25,0,null,null,6,"td",[["class","right"]],[[8,"title",0]],null,null,null,null)),(n()(),iu(16777216,null,null,1,null,ep)),Qu(27,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,rp)),Qu(29,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,op)),Qu(31,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(32,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(33,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(34,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,sp)),Qu(36,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,ap)),Qu(38,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,""!==e.clazz.reportPath),n(t,4,0,""===e.clazz.reportPath),n(t,7,0,null!==e.clazz.currentHistoricCoverage),n(t,9,0,null===e.clazz.currentHistoricCoverage),n(t,12,0,null!==e.clazz.currentHistoricCoverage),n(t,14,0,null===e.clazz.currentHistoricCoverage),n(t,17,0,null!==e.clazz.currentHistoricCoverage),n(t,19,0,null===e.clazz.currentHistoricCoverage),n(t,22,0,null!==e.clazz.currentHistoricCoverage),n(t,24,0,null===e.clazz.currentHistoricCoverage),n(t,27,0,e.clazz.lineCoverageHistory.length>1),n(t,29,0,null!==e.clazz.currentHistoricCoverage),n(t,31,0,null===e.clazz.currentHistoricCoverage),n(t,34,0,e.clazz.coverage),n(t,36,0,e.branchCoverageAvailable),n(t,38,0,e.branchCoverageAvailable)},function(n,t){n(t,25,0,t.component.clazz.coverageType)})}var fp=Pl({encapsulation:2,styles:[],data:{}});function hp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.noGrouping)})}function pp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.byAssembly)})}function dp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){var e=t.component;n(t,1,0,e.translations.byNamespace+" "+e.settings.grouping)})}function gp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function vp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"br",[],null,null,null,null,null))],null,null)}function yp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageIncreaseOnly"),n(t,2,0,"branchCoverageIncreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageIncreaseOnly)})}function mp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageDecreaseOnly"),n(t,2,0,"branchCoverageDecreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageDecreaseOnly)})}function bp(n){return bs(0,[(n()(),lu(0,0,null,null,26,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,25,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,2).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,2).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionType=e)&&r),r},null,null)),Qu(2,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(4,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(6,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(7,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(8,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(9,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(10,null,["",""])),(n()(),lu(11,0,null,null,3,"option",[["value","allChanges"]],null,null,null,null,null)),Qu(12,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(13,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(14,null,["",""])),(n()(),lu(15,0,null,null,3,"option",[["value","lineCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(16,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(17,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(18,null,["",""])),(n()(),lu(19,0,null,null,3,"option",[["value","lineCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(20,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(21,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(22,null,["",""])),(n()(),iu(16777216,null,null,1,null,yp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mp)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,4,0,e.settings.historyComparisionType),n(t,8,0,""),n(t,9,0,""),n(t,12,0,"allChanges"),n(t,13,0,"allChanges"),n(t,16,0,"lineCoverageIncreaseOnly"),n(t,17,0,"lineCoverageIncreaseOnly"),n(t,20,0,"lineCoverageDecreaseOnly"),n(t,21,0,"lineCoverageDecreaseOnly"),n(t,24,0,e.branchCoverageAvailable),n(t,26,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,1,0,Vu(t,6).ngClassUntouched,Vu(t,6).ngClassTouched,Vu(t,6).ngClassPristine,Vu(t,6).ngClassDirty,Vu(t,6).ngClassValid,Vu(t,6).ngClassInvalid,Vu(t,6).ngClassPending),n(t,10,0,e.translations.filter),n(t,14,0,e.translations.allChanges),n(t,18,0,e.translations.lineCoverageIncreaseOnly),n(t,22,0,e.translations.lineCoverageDecreaseOnly)})}function _p(n){return bs(0,[(n()(),lu(0,0,null,null,18,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,13,"div",[],null,null,null,null,null)),(n()(),vs(2,null,[" "," "])),(n()(),lu(3,0,null,null,11,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionDate=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCurrentHistoricCoverage()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,gp)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,vp)),Qu(16,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bp)),Qu(18,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component;n(t,6,0,e.settings.historyComparisionDate),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.historicCoverageExecutionTimes),n(t,16,0,""!==e.settings.historyComparisionDate),n(t,18,0,""!==e.settings.historyComparisionDate)},function(n,t){var e=t.component;n(t,2,0,e.translations.compareHistory),n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.date)})}function wp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null))],null,null)}function Cp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null))],null,null)}function Ep(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"coverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.coverage)})}function xp(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("branchcoverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"branchcoverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"branchcoverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"branchcoverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.branchCoverage)})}function Op(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["codeelement-row",""]],null,null,null,zh,Rh)),Qu(1,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null)],function(n,t){n(t,1,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable)},null)}function kp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Sp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,kp)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ap(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class","namespace"],["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Tp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ap)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ip(n){return bs(0,[(n()(),lu(0,0,null,null,4,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"tr",[["class","namespace"],["codeelement-row",""]],null,null,null,zh,Rh)),Qu(2,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null),(n()(),iu(16777216,null,null,1,null,Tp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){n(t,2,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable),n(t,4,0,t.parent.context.$implicit.classes)},null)}function Pp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ip)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Np(n){return bs(0,[(n()(),lu(0,0,null,null,6,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Op)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Sp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,Pp)),Qu(6,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r),n(t,4,0,t.context.$implicit.classes),n(t,6,0,t.context.$implicit.subElements)},null)}function Dp(n){return bs(0,[(n()(),lu(0,0,null,null,87,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,34,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,5,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.collapseAll(e)&&r),r},null,null)),(n()(),vs(4,null,["",""])),(n()(),vs(-1,null,[" | "])),(n()(),lu(6,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.expandAll(e)&&r),r},null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,15,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,hp)),Qu(10,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,pp)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,dp)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,0,"br",[],null,null,null,null,null)),(n()(),vs(16,null,[" "," "])),(n()(),lu(17,0,null,null,6,"input",[["min","-1"],["step","1"],["type","range"]],[[8,"max",0],[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"],[null,"change"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,18)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,18).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,18)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,18)._compositionEnd(e.target.value)&&r),"change"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"input"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,19).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.grouping=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCoverageInfo()&&r),r},null,null)),Qu(18,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Qu(19,16384,null,0,Vf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n,t){return[n,t]},[Sf,Vf]),Qu(21,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(23,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(24,0,null,null,2,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,_p)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(27,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(28,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(29,null,[""," "])),(n()(),lu(30,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,31)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,31).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,31)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,31)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),r},null,null)),Qu(31,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(33,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(35,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(36,0,null,null,51,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(37,0,null,null,11,"colgroup",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,0,"col",[["class","column90"]],null,null,null,null,null)),(n()(),lu(40,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null)),(n()(),lu(41,0,null,null,0,"col",[["class","column100"]],null,null,null,null,null)),(n()(),lu(42,0,null,null,0,"col",[["class","column70"]],null,null,null,null,null)),(n()(),lu(43,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null)),(n()(),lu(44,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,wp)),Qu(46,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Cp)),Qu(48,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(49,0,null,null,35,"thead",[],null,null,null,null,null)),(n()(),lu(50,0,null,null,34,"tr",[],null,null,null,null,null)),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("name",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),lu(57,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(58,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("covered",e)&&r),r},null,null)),(n()(),lu(59,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(60,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(61,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(62,null,["",""])),(n()(),lu(63,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(64,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("uncovered",e)&&r),r},null,null)),(n()(),lu(65,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(66,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(67,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(68,null,["",""])),(n()(),lu(69,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(70,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverable",e)&&r),r},null,null)),(n()(),lu(71,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(72,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(73,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(74,null,["",""])),(n()(),lu(75,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(76,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("total",e)&&r),r},null,null)),(n()(),lu(77,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(78,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(79,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(80,null,["",""])),(n()(),iu(16777216,null,null,1,null,Ep)),Qu(82,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,xp)),Qu(84,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(85,0,null,null,2,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Np)),Qu(87,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){var e=t.component;n(t,10,0,-1===e.settings.grouping),n(t,12,0,0===e.settings.grouping),n(t,14,0,e.settings.grouping>0),n(t,21,0,e.settings.grouping),n(t,26,0,e.historicCoverageExecutionTimes.length>0),n(t,33,0,e.settings.filter),n(t,46,0,e.branchCoverageAvailable),n(t,48,0,e.branchCoverageAvailable);var r=n(t,55,0,"name"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"name"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"name"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",r);var o=n(t,61,0,"covered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"covered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"covered"!==e.settings.sortBy);n(t,60,0,"icon-down-dir",o);var i=n(t,67,0,"uncovered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"uncovered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"uncovered"!==e.settings.sortBy);n(t,66,0,"icon-down-dir",i);var l=n(t,73,0,"coverable"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverable"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverable"!==e.settings.sortBy);n(t,72,0,"icon-down-dir",l);var u=n(t,79,0,"total"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"total"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"total"!==e.settings.sortBy);n(t,78,0,"icon-down-dir",u),n(t,82,0,e.branchCoverageAvailable),n(t,84,0,e.branchCoverageAvailable),n(t,87,0,e.codeElements)},function(n,t){var e=t.component;n(t,4,0,e.translations.collapseAll),n(t,7,0,e.translations.expandAll),n(t,16,0,e.translations.grouping),n(t,17,0,e.settings.groupingMaximum,Vu(t,23).ngClassUntouched,Vu(t,23).ngClassTouched,Vu(t,23).ngClassPristine,Vu(t,23).ngClassDirty,Vu(t,23).ngClassValid,Vu(t,23).ngClassInvalid,Vu(t,23).ngClassPending),n(t,29,0,e.translations.filter),n(t,30,0,Vu(t,35).ngClassUntouched,Vu(t,35).ngClassTouched,Vu(t,35).ngClassPristine,Vu(t,35).ngClassDirty,Vu(t,35).ngClassValid,Vu(t,35).ngClassInvalid,Vu(t,35).ngClassPending),n(t,56,0,e.translations.name),n(t,62,0,e.translations.covered),n(t,68,0,e.translations.uncovered),n(t,74,0,e.translations.coverable),n(t,80,0,e.translations.total)})}function Mp(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,Dp)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.codeElements.length>0)},null)}function Vp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"coverage-info",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Mp,fp)),Qu(1,114688,null,0,ja,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Hp=xu("coverage-info",ja,Vp,{},{},[]),Rp=ka(Aa,[Na,ja],function(n){return function(n){for(var t={},e=[],r=!1,o=0;odiv { width: 25%; display: inline-block; } +.customizebox div.right input { font-size: 0.8em; width: 150px; } +#namespaceslider { width: 200px; display: inline-block; margin-left: 8px; } + +.percentagebarundefined { + border-left: 2px solid #fff; + padding-left: 3px; +} +.percentagebar0 { + border-left: 2px solid #c10909; + padding-left: 3px; +} +.percentagebar10 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 90%, #0aad0a 90%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar20 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 80%, #0aad0a 80%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar30 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 70%, #0aad0a 70%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar40 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 60%, #0aad0a 60%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar50 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 50%, #0aad0a 50%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar60 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 40%, #0aad0a 40%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar70 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 30%, #0aad0a 30%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar80 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 20%, #0aad0a 20%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar90 { + border-left: 2px solid; + border-image: linear-gradient(to bottom, #c10909 10%, #0aad0a 10%, #0aad0a 100%) 1; + padding-left: 3px; +} +.percentagebar100 { + border-left: 2px solid #0aad0a; + padding-left: 3px; +} + +.hidden, .ng-hide { display: none; } +.right { text-align: right; } +.center { text-align: center; } +.rightmargin { padding-right: 8px; } +.leftmargin { padding-left: 5px; } +.green { background-color: #0aad0a; } +.lightgreen { background-color: #dcf4dc; } +.red { background-color: #c10909; } +.lightred { background-color: #f7dede; } +.orange { background-color: #FFA500; } +.lightorange { background-color: #FFEFD5; } +.gray { background-color: #dcdcdc; } +.lightgray { color: #888888; } +.lightgraybg { background-color: #dadada; } + +.toggleZoom { text-align:right; } + +.ct-chart { position: relative; } +.ct-chart .ct-line { stroke-width: 2px !important; } +.ct-chart .ct-point { stroke-width: 6px !important; transition: stroke-width .2s; } +.ct-chart .ct-point:hover { stroke-width: 10px !important; } +.ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { stroke: #c00 !important;} +.ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { stroke: #1c2298 !important;} + +.tinylinecoveragechart, .tinybranchcoveragechart { background-color: #fff; margin-left: -3px; float: left; border: solid 1px #c1c1c1; width: 30px; height: 18px; } +.historiccoverageoffset { margin-top: 7px; } + +.tinylinecoveragechart .ct-line, .tinybranchcoveragechart .ct-line { stroke-width: 1px !important; } +.tinybranchcoveragechart .ct-series.ct-series-a .ct-line { stroke: #1c2298 !important; } + +.linecoverage { background-color: #c00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } +.branchcoverage { background-color: #1c2298; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } + +.tooltip { position: absolute; display: none; padding: 5px; background: #F4C63D;color: #453D3F; pointer-events: none; z-index: 1; } +.tooltip:after { content: ""; position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin-left: -15px; border: 15px solid transparent; border-top-color: #F4C63D; } + +.column1324 { max-width: 1324px; } +.column674 { max-width: 674px; } +.column60 { width: 60px; } +.column70 { width: 70px; } +.column90 { width: 90px; } +.column98 { width: 98px; } +.column100 { width: 100px; } +.column105 { width: 105px; } +.column112 { width: 112px; } +.column135 { width: 135px; } +.column150 { width: 150px; } + +.covered0 { width: 0px; } +.covered1 { width: 1px; } +.covered2 { width: 2px; } +.covered3 { width: 3px; } +.covered4 { width: 4px; } +.covered5 { width: 5px; } +.covered6 { width: 6px; } +.covered7 { width: 7px; } +.covered8 { width: 8px; } +.covered9 { width: 9px; } +.covered10 { width: 10px; } +.covered11 { width: 11px; } +.covered12 { width: 12px; } +.covered13 { width: 13px; } +.covered14 { width: 14px; } +.covered15 { width: 15px; } +.covered16 { width: 16px; } +.covered17 { width: 17px; } +.covered18 { width: 18px; } +.covered19 { width: 19px; } +.covered20 { width: 20px; } +.covered21 { width: 21px; } +.covered22 { width: 22px; } +.covered23 { width: 23px; } +.covered24 { width: 24px; } +.covered25 { width: 25px; } +.covered26 { width: 26px; } +.covered27 { width: 27px; } +.covered28 { width: 28px; } +.covered29 { width: 29px; } +.covered30 { width: 30px; } +.covered31 { width: 31px; } +.covered32 { width: 32px; } +.covered33 { width: 33px; } +.covered34 { width: 34px; } +.covered35 { width: 35px; } +.covered36 { width: 36px; } +.covered37 { width: 37px; } +.covered38 { width: 38px; } +.covered39 { width: 39px; } +.covered40 { width: 40px; } +.covered41 { width: 41px; } +.covered42 { width: 42px; } +.covered43 { width: 43px; } +.covered44 { width: 44px; } +.covered45 { width: 45px; } +.covered46 { width: 46px; } +.covered47 { width: 47px; } +.covered48 { width: 48px; } +.covered49 { width: 49px; } +.covered50 { width: 50px; } +.covered51 { width: 51px; } +.covered52 { width: 52px; } +.covered53 { width: 53px; } +.covered54 { width: 54px; } +.covered55 { width: 55px; } +.covered56 { width: 56px; } +.covered57 { width: 57px; } +.covered58 { width: 58px; } +.covered59 { width: 59px; } +.covered60 { width: 60px; } +.covered61 { width: 61px; } +.covered62 { width: 62px; } +.covered63 { width: 63px; } +.covered64 { width: 64px; } +.covered65 { width: 65px; } +.covered66 { width: 66px; } +.covered67 { width: 67px; } +.covered68 { width: 68px; } +.covered69 { width: 69px; } +.covered70 { width: 70px; } +.covered71 { width: 71px; } +.covered72 { width: 72px; } +.covered73 { width: 73px; } +.covered74 { width: 74px; } +.covered75 { width: 75px; } +.covered76 { width: 76px; } +.covered77 { width: 77px; } +.covered78 { width: 78px; } +.covered79 { width: 79px; } +.covered80 { width: 80px; } +.covered81 { width: 81px; } +.covered82 { width: 82px; } +.covered83 { width: 83px; } +.covered84 { width: 84px; } +.covered85 { width: 85px; } +.covered86 { width: 86px; } +.covered87 { width: 87px; } +.covered88 { width: 88px; } +.covered89 { width: 89px; } +.covered90 { width: 90px; } +.covered91 { width: 91px; } +.covered92 { width: 92px; } +.covered93 { width: 93px; } +.covered94 { width: 94px; } +.covered95 { width: 95px; } +.covered96 { width: 96px; } +.covered97 { width: 97px; } +.covered98 { width: 98px; } +.covered99 { width: 99px; } +.covered100 { width: 100px; } + + @media print { + html, body { background-color: #fff; } + .container { max-width: 100%; width: 100%; padding: 0; } + .overview colgroup col:first-child { width: 300px; } +} + +.icon-up-dir_active { + background-image: url(icon_up-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDEyMTZxMCAyNi0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMC00NS0xOXQtMTktNDUgMTktNDVsNDQ4LTQ0OHExOS0xOSA0NS0xOXQ0NSAxOWw0NDggNDQ4cTE5IDE5IDE5IDQ1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-down-dir_active { + background-image: url(icon_up-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-down-dir { + background-image: url(icon_down-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-info-circled { + background-image: url(icon_info-circled.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; +} +.icon-plus { + background-image: url(icon_plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTQxNnY0MTZxMCA0MC0yOCA2OHQtNjggMjhoLTE5MnEtNDAgMC02OC0yOHQtMjgtNjh2LTQxNmgtNDE2cS00MCAwLTY4LTI4dC0yOC02OHYtMTkycTAtNDAgMjgtNjh0NjgtMjhoNDE2di00MTZxMC00MCAyOC02OHQ2OC0yOGgxOTJxNDAgMCA2OCAyOHQyOCA2OHY0MTZoNDE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-minus { + background-image: url(icon_minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTEyMTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGgxMjE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); + background-repeat: no-repeat; + background-size: contain; + padding-left: 15px; + height: 0.9em; + display: inline-block; + position: relative; + top: 3px; +} +.icon-wrench { + background-image: url(icon_wrench.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTQ0OCAxNDcycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em02NDQtNDIwbC02ODIgNjgycS0zNyAzNy05MCAzNy01MiAwLTkxLTM3bC0xMDYtMTA4cS0zOC0zNi0zOC05MCAwLTUzIDM4LTkxbDY4MS02ODFxMzkgOTggMTE0LjUgMTczLjV0MTczLjUgMTE0LjV6bTYzNC00MzVxMCAzOS0yMyAxMDYtNDcgMTM0LTE2NC41IDIxNy41dC0yNTguNSA4My41cS0xODUgMC0zMTYuNS0xMzEuNXQtMTMxLjUtMzE2LjUgMTMxLjUtMzE2LjUgMzE2LjUtMTMxLjVxNTggMCAxMjEuNSAxNi41dDEwNy41IDQ2LjVxMTYgMTEgMTYgMjh0LTE2IDI4bC0yOTMgMTY5djIyNGwxOTMgMTA3cTUtMyA3OS00OC41dDEzNS41LTgxIDcwLjUtMzUuNXExNSAwIDIzLjUgMTB0OC41IDI1eiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-fork { + background-image: url(icon_fork.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNmZmYiIC8+PHBhdGggZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-cube { + background-image: url(icon_cube.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTg5NiAxNjI5bDY0MC0zNDl2LTYzNmwtNjQwIDIzM3Y3NTJ6bS02NC04NjVsNjk4LTI1NC02OTgtMjU0LTY5OCAyNTR6bTgzMi0yNTJ2NzY4cTAgMzUtMTggNjV0LTQ5IDQ3bC03MDQgMzg0cS0yOCAxNi02MSAxNnQtNjEtMTZsLTcwNC0zODRxLTMxLTE3LTQ5LTQ3dC0xOC02NXYtNzY4cTAtNDAgMjMtNzN0NjEtNDdsNzA0LTI1NnEyMi04IDQ0LTh0NDQgOGw3MDQgMjU2cTM4IDE0IDYxIDQ3dDIzIDczeiIvPjwvc3ZnPg==); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-search-plus { + background-image: url(icon_search-plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtMjI0djIyNHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di0yMjRoLTIyNHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTY0cTAtMTMgOS41LTIyLjV0MjIuNS05LjVoMjI0di0yMjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41djIyNGgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} +.icon-search-minus { + background-image: url(icon_search-minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNTc2cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg1NzZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + padding-left: 20px; + height: 0.9em; + display: inline-block; +} + +.ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} \ No newline at end of file diff --git a/Host/ConsoleAppNet5/ConsoleAppNet5.csproj b/Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj similarity index 100% rename from Host/ConsoleAppNet5/ConsoleAppNet5.csproj rename to Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj diff --git a/Host/NetFx48/Program.cs b/Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs similarity index 51% rename from Host/NetFx48/Program.cs rename to Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs index 9aeab0ca..be1b5acd 100644 --- a/Host/NetFx48/Program.cs +++ b/Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs @@ -1,10 +1,10 @@ using System; -namespace NetFx48 +namespace ConsoleApp1 { - internal class Program + class Program { - private static void Main(string[] args) + static void Main(string[] args) { Console.WriteLine("Hello World!"); } diff --git a/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json new file mode 100644 index 00000000..053b519b --- /dev/null +++ b/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ConsoleApp1": { + "commandName": "Project", + "commandLineArgs": "-" + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/NetFx48/Properties/launchSettings.json new file mode 100644 index 00000000..e4d12871 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "NetFx48": { + "commandName": "Project", + "commandLineArgs": "--AppId=1234567890" + } + } +} \ No newline at end of file diff --git a/Host/NetFx48/NetFx48.csproj b/Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj similarity index 59% rename from Host/NetFx48/NetFx48.csproj rename to Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj index bd4231b7..9590466a 100644 --- a/Host/NetFx48/NetFx48.csproj +++ b/Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj @@ -4,8 +4,5 @@ Exe net5.0 - - - diff --git a/Host/ConsoleAppNet5/Program.cs b/Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs similarity index 50% rename from Host/ConsoleAppNet5/Program.cs rename to Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs index 5c83e5b0..be1b5acd 100644 --- a/Host/ConsoleAppNet5/Program.cs +++ b/Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs @@ -1,10 +1,10 @@ using System; -namespace ConsoleAppNet5 +namespace ConsoleApp1 { - internal class Program + class Program { - private static void Main(string[] args) + static void Main(string[] args) { Console.WriteLine("Hello World!"); } diff --git a/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/WebApi.NetFx48.csproj.DotSettings b/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/WebApi.NetFx48.csproj.DotSettings new file mode 100644 index 00000000..37e9881c --- /dev/null +++ b/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/WebApi.NetFx48.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Host/ConsoleAppNet48/AppHost.cs b/Host/ConsoleAppNet48/AppHost.cs deleted file mode 100644 index 8e713810..00000000 --- a/Host/ConsoleAppNet48/AppHost.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class AppHost : IHostedService - { - private readonly ILogger logger; - private readonly IHostApplicationLifetime appLifetime; - - public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) - { - this.logger = logger; - this.appLifetime = appLifetime; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); - - await Task.Yield(); - - this.appLifetime.StopApplication(); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/ConsoleAppNet48.csproj b/Host/ConsoleAppNet48/ConsoleAppNet48.csproj deleted file mode 100644 index 50724ce9..00000000 --- a/Host/ConsoleAppNet48/ConsoleAppNet48.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - net48 - - - - - - - diff --git a/Host/ConsoleAppNet48/LabBackgroundService.cs b/Host/ConsoleAppNet48/LabBackgroundService.cs deleted file mode 100644 index ccca500a..00000000 --- a/Host/ConsoleAppNet48/LabBackgroundService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class LabBackgroundService : BackgroundService - { - private readonly ILogger _logger; - - public LabBackgroundService(ILogger logger) - { - this._logger = logger; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - await Task.Delay(1000, stoppingToken); - } - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/LabHostedService.cs b/Host/ConsoleAppNet48/LabHostedService.cs deleted file mode 100644 index b580eb10..00000000 --- a/Host/ConsoleAppNet48/LabHostedService.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace ConsoleAppNet48 -{ - public class LabHostedService : IHostedService - { - private readonly ILogger _logger; - - public LabHostedService(ILogger logger, - IHostApplicationLifetime lifetime) - { - this._logger = logger; - - lifetime.ApplicationStarted.Register(this.OnStarted); - lifetime.ApplicationStopping.Register(this.OnStopping); - lifetime.ApplicationStopped.Register(this.OnStopped); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("1. StartAsync has been called."); - - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("4. StopAsync has been called."); - - return Task.CompletedTask; - } - - private void OnStarted() - { - this._logger.LogInformation("2. OnStarted has been called."); - } - - private void OnStopped() - { - this._logger.LogInformation("5. OnStopped has been called."); - } - - private void OnStopping() - { - this._logger.LogInformation("3. OnStopping has been called."); - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNet48/Program.cs b/Host/ConsoleAppNet48/Program.cs deleted file mode 100644 index ee76ac29..00000000 --- a/Host/ConsoleAppNet48/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace ConsoleAppNet48 -{ - public class Program - { - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureServices((hostContext, services) => - { - services.AddHostedService(); - }); - } - - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - } -} \ No newline at end of file diff --git a/Host/ConsoleAppNetFx48/AppHost.cs b/Host/Lab.MsHost/ConsoleAppNetFx48/AppHost.cs similarity index 100% rename from Host/ConsoleAppNetFx48/AppHost.cs rename to Host/Lab.MsHost/ConsoleAppNetFx48/AppHost.cs diff --git a/Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/Lab.MsHost/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj similarity index 100% rename from Host/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj rename to Host/Lab.MsHost/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj diff --git a/Host/ConsoleAppNetFx48/LabBackgroundService.cs b/Host/Lab.MsHost/ConsoleAppNetFx48/LabBackgroundService.cs similarity index 100% rename from Host/ConsoleAppNetFx48/LabBackgroundService.cs rename to Host/Lab.MsHost/ConsoleAppNetFx48/LabBackgroundService.cs diff --git a/Host/ConsoleAppNetFx48/LabHostedService.cs b/Host/Lab.MsHost/ConsoleAppNetFx48/LabHostedService.cs similarity index 100% rename from Host/ConsoleAppNetFx48/LabHostedService.cs rename to Host/Lab.MsHost/ConsoleAppNetFx48/LabHostedService.cs diff --git a/Host/ConsoleAppNetFx48/Program.cs b/Host/Lab.MsHost/ConsoleAppNetFx48/Program.cs similarity index 100% rename from Host/ConsoleAppNetFx48/Program.cs rename to Host/Lab.MsHost/ConsoleAppNetFx48/Program.cs diff --git a/Host/Lab.MsHost.sln b/Host/Lab.MsHost/Lab.MsHost.sln similarity index 100% rename from Host/Lab.MsHost.sln rename to Host/Lab.MsHost/Lab.MsHost.sln diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj new file mode 100644 index 00000000..1a8ca194 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -0,0 +1,17 @@ + + + + net48 + bin + bin\ConsoleAppNetFx48.xml + dotnet-ConsoleAppNetFx48-525DDA0C-18EF-4AE3-A405-A9653AA2D910 + + + + + + + + + + diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/DoThing.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/DoThing.cs new file mode 100644 index 00000000..a5347b9f --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/DoThing.cs @@ -0,0 +1,38 @@ +// using System; +// using System.Timers; +// using NLog; +// +// namespace ConsoleAppNetFx48 +// { +// public class DoThing +// { +// private static readonly ILogger s_logger; +// private readonly Timer _timer; +// +// static DoThing() +// { +// if (s_logger == null) +// { +// s_logger = LogManager.GetCurrentClassLogger(); +// } +// } +// +// public DoThing() +// { +// this._timer = new Timer(1000) {AutoReset = true}; +// this._timer.Elapsed += (sender, eventArgs) => Console.WriteLine($"Now Time:{DateTime.Now}"); +// } +// +// public void Start() +// { +// this._timer.Start(); +// s_logger.Trace("Timer Start"); +// } +// +// public void Stop() +// { +// this._timer.Stop(); +// s_logger.Trace("Timer Stop"); +// } +// } +// } \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs new file mode 100644 index 00000000..cc743c08 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace ConsoleAppNetFx48 +{ + public class Program + { + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseWindowsService() + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }); + + public static void Main(string[] args) + { + // HostFactory.Run(x => + // { + // x.Service(s => + // { + // s.ConstructUsing(name => new DoThing()); + // s.WhenStarted(tc => tc.Start()); + // s.WhenStopped(tc => tc.Stop()); + // }); + // x.UseNLog(); + // x.RunAsLocalSystem(); + // var assemblyName = Assembly.GetEntryAssembly().GetName().Name; + // x.SetDescription("Sample Topshelf Host"); + // x.SetDisplayName(assemblyName); + // x.SetServiceName(assemblyName); + // }); + + var hostBuilder = CreateHostBuilder(args); + hostBuilder.Build().Run(); + } + } +} \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Properties/launchSettings.json b/Host/Lab.WorkerService/ConsoleAppNetFx48/Properties/launchSettings.json new file mode 100644 index 00000000..b1932b1c --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "ConsoleAppNetFx48": { + "commandName": "Project", + "dotnetRunMessages": "true", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Host/NetFx48/LabBackgroundService.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs similarity index 70% rename from Host/NetFx48/LabBackgroundService.cs rename to Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs index 69048bdd..74b3e236 100644 --- a/Host/NetFx48/LabBackgroundService.cs +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs @@ -1,16 +1,16 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace NetFx48 +namespace ConsoleAppNetFx48 { - public class LabBackgroundService : BackgroundService + public class Worker : BackgroundService { - private readonly ILogger _logger; + private readonly ILogger _logger; - public LabBackgroundService(ILogger logger) + public Worker(ILogger logger) { this._logger = logger; } diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.Development.json b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Host/Lab.WorkerService/Lab.WorkerService.sln b/Host/Lab.WorkerService/Lab.WorkerService.sln new file mode 100644 index 00000000..80856a97 --- /dev/null +++ b/Host/Lab.WorkerService/Lab.WorkerService.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppNetFx48", "ConsoleAppNetFx48\ConsoleAppNetFx48.csproj", "{6E527E3E-6180-4250-B61D-0B69208737C8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E527E3E-6180-4250-B61D-0B69208737C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E527E3E-6180-4250-B61D-0B69208737C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E527E3E-6180-4250-B61D-0B69208737C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E527E3E-6180-4250-B61D-0B69208737C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Host/NetFx48/AppHost.cs b/Host/NetFx48/AppHost.cs deleted file mode 100644 index 898c3be4..00000000 --- a/Host/NetFx48/AppHost.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace NetFx48 -{ - public class AppHost : IHostedService - { - private readonly IHostApplicationLifetime appLifetime; - private readonly ILogger logger; - - public AppHost(ILogger logger, IHostApplicationLifetime appLifetime) - { - this.logger = logger; - this.appLifetime = appLifetime; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App running at: {time}", DateTimeOffset.Now); - - await Task.Yield(); - - this.appLifetime.StopApplication(); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this.logger.LogWarning("App stopped at: {time}", DateTimeOffset.Now); - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Host/NetFx48/LabHostedService.cs b/Host/NetFx48/LabHostedService.cs deleted file mode 100644 index 92cec54d..00000000 --- a/Host/NetFx48/LabHostedService.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace NetFx48 -{ - public class LabHostedService : IHostedService - { - private readonly ILogger _logger; - - public LabHostedService(ILogger logger, - IHostApplicationLifetime lifetime) - { - this._logger = logger; - - lifetime.ApplicationStarted.Register(this.OnStarted); - lifetime.ApplicationStopping.Register(this.OnStopping); - lifetime.ApplicationStopped.Register(this.OnStopped); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("1. StartAsync has been called."); - - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - this._logger.LogInformation("4. StopAsync has been called."); - - return Task.CompletedTask; - } - - private void OnStarted() - { - this._logger.LogInformation("2. OnStarted has been called."); - } - - private void OnStopped() - { - this._logger.LogInformation("5. OnStopped has been called."); - } - - private void OnStopping() - { - this._logger.LogInformation("3. OnStopping has been called."); - } - } -} \ No newline at end of file diff --git a/ORM/Linq2Db/Lab.Linq2Db/Lab.UnitTest/EntityModel/CopyMe.SqlServer.generated.cs b/ORM/Linq2Db/Lab.Linq2Db/Lab.UnitTest/EntityModel/CopyMe.SqlServer.generated.cs new file mode 100644 index 00000000..092071d2 --- /dev/null +++ b/ORM/Linq2Db/Lab.Linq2Db/Lab.UnitTest/EntityModel/CopyMe.SqlServer.generated.cs @@ -0,0 +1,216 @@ +//--------------------------------------------------------------------------------------------------- +// +// This code was generated by T4Model template for T4 (https://github.com/linq2db/linq2db). +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. +// +//--------------------------------------------------------------------------------------------------- + +#pragma warning disable 1591 + +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +using LinqToDB; +using LinqToDB.Common; +using LinqToDB.Data; +using LinqToDB.DataProvider.SqlServer; +using LinqToDB.Extensions; +using LinqToDB.Mapping; + +namespace Lab.EntityModel +{ + /// + /// Database : LabEmployee2 + /// Data Source : (localdb)\mssqllocaldb + /// Server Version : 13.00.4001 + /// + public partial class LabEmployee2DB : LinqToDB.Data.DataConnection + { + public ITable Employees { get { return this.GetTable(); } } + public ITable Identities { get { return this.GetTable(); } } + public ITable Orders { get { return this.GetTable(); } } + + public LabEmployee2DB() + { + InitDataContext(); + InitMappingSchema(); + } + + public LabEmployee2DB(string configuration) + : base(configuration) + { + InitDataContext(); + InitMappingSchema(); + } + + partial void InitDataContext (); + partial void InitMappingSchema(); + + #region FreeTextTable + + public class FreeTextKey + { + public T Key; + public int Rank; + } + + private static MethodInfo _freeTextTableMethod1 = typeof(LabEmployee2DB).GetMethod("FreeTextTable", new Type[] { typeof(string), typeof(string) }); + + [FreeTextTableExpression] + public ITable> FreeTextTable(string field, string text) + { + return this.GetTable>( + this, + _freeTextTableMethod1, + field, + text); + } + + private static MethodInfo _freeTextTableMethod2 = + typeof(LabEmployee2DB).GetMethods() + .Where(m => m.Name == "FreeTextTable" && m.IsGenericMethod && m.GetParameters().Length == 2) + .Where(m => m.GetParameters()[0].ParameterType.IsGenericTypeEx() && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)) + .Where(m => m.GetParameters()[1].ParameterType == typeof(string)) + .Single(); + + [FreeTextTableExpression] + public ITable> FreeTextTable(Expression> fieldSelector, string text) + { + return this.GetTable>( + this, + _freeTextTableMethod2, + fieldSelector, + text); + } + + #endregion + } + + [Table(Schema="dbo", Name="Employee")] + public partial class Employee + { + [PrimaryKey, NotNull ] public Guid Id { get; set; } // uniqueidentifier + [Column, Nullable] public string Name { get; set; } // nvarchar(50) + [Column, Nullable] public int? Age { get; set; } // int + [Identity ] public long SequenceId { get; set; } // bigint + [Column, Nullable] public string Remark { get; set; } // nvarchar(50) + + #region Associations + + /// + /// FK_Identity_Employee_Id_BackReference + /// + [Association(ThisKey="Id", OtherKey="EmployeeId", CanBeNull=true, Relationship=Relationship.OneToOne, IsBackReference=true)] + public Identity IdentityId { get; set; } + + /// + /// FK_Order_Employee_id_BackReference + /// + [Association(ThisKey="Id", OtherKey="EmployeeId", CanBeNull=true, Relationship=Relationship.OneToMany, IsBackReference=true)] + public IEnumerable Orderids { get; set; } + + #endregion + } + + [Table(Schema="dbo", Name="Identity")] + public partial class Identity + { + [Column("Employee_Id"), PrimaryKey, NotNull] public Guid EmployeeId { get; set; } // uniqueidentifier + [Column(), NotNull] public string Account { get; set; } // nvarchar(50) + [Column(), NotNull] public string Password { get; set; } // nvarchar(50) + [Column(), Identity ] public long SequenceId { get; set; } // bigint + [Column(), Nullable ] public string Remark { get; set; } // nvarchar(50) + + #region Associations + + /// + /// FK_Identity_Employee_Id + /// + [Association(ThisKey="EmployeeId", OtherKey="Id", CanBeNull=false, Relationship=Relationship.OneToOne, KeyName="FK_Identity_Employee_Id", BackReferenceName="IdentityId")] + public Employee Employee { get; set; } + + #endregion + } + + [Table(Schema="dbo", Name="Order")] + public partial class Order + { + [Column(), PrimaryKey, NotNull] public Guid Id { get; set; } // uniqueidentifier + [Column("Employee_Id"), Nullable ] public Guid? EmployeeId { get; set; } // uniqueidentifier + [Column(), Nullable ] public DateTime? OrderTime { get; set; } // datetime + [Column(), Nullable ] public string Remark { get; set; } // nvarchar(50) + [Column(), Identity ] public long SequenceId { get; set; } // bigint + + #region Associations + + /// + /// FK_Order_Employee_id + /// + [Association(ThisKey="EmployeeId", OtherKey="Id", CanBeNull=true, Relationship=Relationship.ManyToOne, KeyName="FK_Order_Employee_id", BackReferenceName="Orderids")] + public Employee Employee { get; set; } + + #endregion + } + + public static partial class LabEmployee2DBStoredProcedures + { + #region GetAllEmployee + + public static IEnumerable GetAllEmployee(this DataConnection dataConnection) + { + return dataConnection.QueryProc("[dbo].[GetAllEmployee]"); + } + + #endregion + + #region InsertOrUpdateEmployee + + public static int InsertOrUpdateEmployee(this DataConnection dataConnection, Guid? @Id, string @Name, int? @Age, string @Remark) + { + return dataConnection.ExecuteProc("[dbo].[InsertOrUpdateEmployee]", + new DataParameter("@Id", @Id, DataType.Guid), + new DataParameter("@Name", @Name, DataType.NVarChar), + new DataParameter("@Age", @Age, DataType.Int32), + new DataParameter("@Remark", @Remark, DataType.NVarChar)); + } + + #endregion + + #region InsertOrUpdateEmployee2 + + public static int InsertOrUpdateEmployee2(this DataConnection dataConnection, DataTable @EmployeeType) + { + return dataConnection.ExecuteProc("[dbo].[InsertOrUpdateEmployee2]", + new DataParameter("@EmployeeType", @EmployeeType, DataType.Structured){ DbType = "[dbo].[InsertOrUpdateEmployeeType]" }); + } + + #endregion + } + + public static partial class TableExtensions + { + public static Employee Find(this ITable table, Guid Id) + { + return table.FirstOrDefault(t => + t.Id == Id); + } + + public static Identity Find(this ITable table, Guid EmployeeId) + { + return table.FirstOrDefault(t => + t.EmployeeId == EmployeeId); + } + + public static Order Find(this ITable table, Guid Id) + { + return table.FirstOrDefault(t => + t.Id == Id); + } + } +} + +#pragma warning restore 1591 From d3572e71466e4a03d2d41ebcca0025cdd3b4d003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Wed, 7 Apr 2021 14:08:56 +0800 Subject: [PATCH 039/267] delete --- .../history/2021-03-26_16-34-47_CoverageHistory.xml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml deleted file mode 100644 index 7531ba24..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file From 9d393854e9a3ace48371d0046cc04bae39c9456e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Wed, 7 Apr 2021 14:09:18 +0800 Subject: [PATCH 040/267] delete --- .../report/ClassLibrary1_Calculation.htm | 82 -- .../Lab.OpenCoverDemo/report/Cobertura.xml | 105 -- .../report/UnitTestProject1_UnitTest1.htm | 88 -- .../report/UnitTestProject2_UnitTest1.htm | 75 - .../report/badge_linecoverage.png | Bin 2925 -> 0 bytes .../report/badge_linecoverage.svg | 88 -- .../Lab.OpenCoverDemo/report/class.js | 207 --- .../Lab.OpenCoverDemo/report/coverage.xml | 1270 ----------------- .../Lab.OpenCoverDemo/report/icon_cube.svg | 2 - .../report/icon_down-dir_active.svg | 2 - .../Lab.OpenCoverDemo/report/icon_fork.svg | 2 - .../report/icon_info-circled.svg | 2 - .../Lab.OpenCoverDemo/report/icon_minus.svg | 2 - .../Lab.OpenCoverDemo/report/icon_plus.svg | 2 - .../report/icon_search-minus.svg | 2 - .../report/icon_search-plus.svg | 2 - .../Lab.OpenCoverDemo/report/icon_up-dir.svg | 2 - .../report/icon_up-dir_active.svg | 2 - .../Lab.OpenCoverDemo/report/icon_wrench.svg | 2 - .../Lab.OpenCoverDemo/report/index.htm | 71 - .../Lab.OpenCoverDemo/report/main.js | 274 ---- .../Lab.OpenCoverDemo/report/report.css | 357 ----- 22 files changed, 2639 deletions(-) delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/report.css diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm deleted file mode 100644 index 6976b753..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - -ClassLibrary1.Calculation - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:ClassLibrary1.Calculation
Assembly:ClassLibrary1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs
Covered lines:3
Uncovered lines:6
Coverable lines:9
Total lines:18
Line coverage:33.3% (3 of 9)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
Add(...)10100%100%1
Sub(...)100%0%2
Sub1(...)100%0%2
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs

- - - - - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1namespace ClassLibrary1
 2{
 3    public class Calculation
 4    {
 5        public int Add(int firstNumber, int secondNumber)
 26        {
 27            return firstNumber + secondNumber;
 28        }
 9        public int Sub(int firstNumber, int secondNumber)
 010        {
 011            return firstNumber + secondNumber;
 012        }
 13        public int Sub1(int firstNumber, int secondNumber)
 014        {
 015            return firstNumber + secondNumber;
 016        }
 17    }
 18}
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml deleted file mode 100644 index 105f9442..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm deleted file mode 100644 index 1ec7556c..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -UnitTestProject1.UnitTest1 - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:UnitTestProject1.UnitTest1
Assembly:UnitTestProject1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs
Covered lines:9
Uncovered lines:1
Coverable lines:10
Total lines:26
Line coverage:90% (9 of 10)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
TestMethod2()1080%100%1.01
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1using System;
 2using ClassLibrary1;
 3using Microsoft.VisualStudio.TestTools.UnitTesting;
 4
 5namespace UnitTestProject1
 6{
 7    [TestClass]
 8    public class UnitTest1
 9    {
 10        [TestMethod]
 11        public void TestMethod1()
 112        {
 113            var calculation = new Calculation();
 114            var actual = calculation.Add(1, 1);
 115            Assert.AreEqual(2,actual);
 116        }
 17
 18        [TestMethod]
 19        public void TestMethod2()
 120        {
 121            var calculation = new Calculation();
 122            var actual = calculation.Add(1, 1);
 123            Assert.AreEqual(1,actual);
 024        }
 25    }
 26}
-
-
-
-

Methods/Properties

-TestMethod1()
-TestMethod2()
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm deleted file mode 100644 index 7d13ebe2..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - -UnitTestProject2.UnitTest1 - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:UnitTestProject2.UnitTest1
Assembly:UnitTestProject2
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs
Covered lines:3
Uncovered lines:0
Coverable lines:3
Total lines:15
Line coverage:100% (3 of 3)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs

- - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1using System;
 2using Microsoft.VisualStudio.TestTools.UnitTesting;
 3
 4namespace UnitTestProject2
 5{
 6    [TestClass]
 7    public class UnitTest1
 8    {
 9        [TestMethod]
 10        public void TestMethod1()
 111        {
 112            Assert.AreEqual(1,1);
 113        }
 14    }
 15}
-
-
-
-

Methods/Properties

-TestMethod1()
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png deleted file mode 100644 index 1d4585190f78c440e6cda1f35623d1780d05fbd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2925 zcmV-z3zGDSP)4*Bbt2V+UCULEJzD1zd5%y@;aL*s6%J_)O5c zMH`=0TcWmr#>6Ey#ywUOHBGv>)C~+G&jQL5P+Ursry}?e*})NkVU~CPgLj+-hM}U3 zPk(a4%vt{P-+R9M{ogrvq!AGjmKGKk2>`xer3*gVTd~-*v<~$bajZk#y-h0-L3FIL zu5yD^CX*#-G@3PkcSQg1#_n+k`|D75Z_~PHpjqo=fNZ6RKvN_0bT{2VfWnnT?YML+XoCbZVYh$Ah3BDHi%g1!vHokJRD-NSX-}F zYan;3hupg!h5bw6mG1_>Y;Q=LB=0ir_tHLeurc$H8r}pk`~LZzJLrDx8cN5HhrP42 zwoUp%3YmpW`@Jlv1`Qqx1fT5%$w$DMw2xq8*fOjb_vjIZJbMPW=g%SCy&IO6maw(8 z{fhxr>Vn*yiMoFXE#y+OYD#fq@5bcj8?f9()*{)}Gq7FMEE8 z%VRDhd2}+OyS1vCxLJrTaANog_}lwy>!x~4Mc%|bq>N5MZ@b>w`t-5s7~wL)>=^Z! z&85B`lZuKUku2n;rQ3W`@ib${yzSScZdxfxp+E2_P3jFW_YHNlZ?kjBopV z%Yip7V;Uy8PeN>;SbTQvGswg;oE~u+1$6~Tt1#XjJ6ky8(BMM|>l}vf^1jo?9N#w{ zk^hcFh*Jnw`L4p!n@cg!aUk3*-Eg(ys@d0|2ex61(JVzwu}NO1#6kP_qhRo0_yz|< z%7E4q*vKk}*1`T!+Z%=@OP0XN$qA7Rz%O3BU^-BZ$&)9;-rin&an{z>oKXpk<{E*x zQ>RX-sHiZRvgko_badqNl7GX#&P+BmHa4QJuFhyI#jQ{%T5M~}4ChH_*ZU@>>u!SE z1k|hQk&u@_LL9MoV-e~S%IEa6^FwY;E^?}Kc;DRGTm;w$;E(b@^!(XKY;b+Tb^KWH zBZfH-BSo)`+1Jh&g>{ASu=K#ET|VXYKM(sEk(rV0k_UQV3uZT8Dv=m=fYbNiL;1@u ztc@|7tYxq%d{j_NG!Q9UpUigs9^K=MgIr+eb+>WcmBK}$_dg{P+{)~#EI z=ZuZq+}x0zosC0>4w-$E$=;@B8{jwqSyfs1#{U~k?=l^kRhfv-i|2EmEmabj9qN=J&%M3z2fs8;* zfT2FKX3fI<`SX#Lm4$ip=Ao>t4A-w;$Cxo=czTOf+8bPk^kftkn7>@D3k7oHH41 zdM+|eN__(Z1NmG6+X~|;?e^=}53^^_=Bb~I#(8;pp`f6k)oV4nHwM=`_T@{n$C6#K z(bvPe?g%`0Ywu#ioelhZUTq$VrWL_a=7?><+mKwEjJQAJc-!Wn&Dh?5JEE>dnT{8) z*Rry)5}Wfj^Uo9AC*rp!zoA}PkHj&F*nWRIlEx%q*rj2pZK^ftQx9x4R#xcB^opU? zvXrwkt}CQ=CXP4MY+VmslfAQmEk^ytw8;o;#N z7{VB0q!TAjG~XoFmo%65X=46_vqECE(9lqxIw_SdSg=43n8dKU@$06Nef#!trX-{E zF3e%B8TS2`^_v}t>$5(x1`LI18YRs`jI?UiD*kptsHgNusVO%%7o$gy=5ta#eZFzyMsqq~b*E09;;D?7 zic%ug=j7yI#E20b0HnQWj-EYxwg?(Zzj=9iTn~~a^Y!)Bw$nT_X3XH=>)W?4*LXa= zuvB{c_H7;u`KLncNxuH>^eL1x#b@Ach$ViWF8s}jvaXT@FCY= zB)$m{4Gj%kmg zmKugF{kPy?$U&U`?`cG5M)Ns0{ke&!#H6uFP-#>+`}8cf6YOhlj}rX%(ukWu<1_8=CaBM~7D zQp%({>PrhQ*h{8=2pHs#07U&sSJ6(CKqd4gpio}|BrzlTqq*q4FMSr`qA|yhALjt0 zbW7tXE-NN8C@w(*N?OskEt%o3K0bK`WOFUWKr6rNUwshRY$fcTD&>0hbeR0C;h9-J zj9ynAba6p1c5}U%KG_!M1F?q{OrG)AYM|Wl0-7eZ*?|)f$Y|kdv4u?s#X@cLpY)L+ z>V+7I8J%CfKGThx06>NO(Rk`d?F35Qz5)nq8G8$DLOX35=&+f<4xhj|&d00000NkvXXu0mjf72v4t diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg deleted file mode 100644 index f3f4a54c..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - Code coverage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Generated by: ReportGenerator 4.1.2.0 - - - - Coverage - Coverage - 68.1%68.1% - - - - - Line coverage - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js deleted file mode 100644 index b82bca96..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js +++ /dev/null @@ -1,207 +0,0 @@ -/* Chartist.js 0.11.0 - * Copyright © 2017 Gion Kunz - * Free to use under either the WTFPL license or the MIT license. - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT - */ - -!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { - var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { - "use strict"; function d(a) { - var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { - var i = c.serialMap(b.normalized.series, function () { - return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) - }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") - } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) - } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) - }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a -}); - -var i, l, selectedLine = null; - -/* Navigate to hash without browser history entry */ -var navigateToHash = function () { - if (window.history !== undefined && window.history.replaceState !== undefined) { - window.history.replaceState(undefined, undefined, this.getAttribute("href")); - } -}; - -var hashLinks = document.getElementsByClassName('navigatetohash'); -for (i = 0, l = hashLinks.length; i < l; i++) { - hashLinks[i].addEventListener('click', navigateToHash); -} - -/* Switch test method */ -var switchTestMethod = function () { - var method = this.getAttribute("value"); - console.log("Selected test method: " + method); - - var lines, i, l, coverageData, lineAnalysis, cells; - - lines = document.querySelectorAll('.lineAnalysis tr'); - - for (i = 1, l = lines.length; i < l; i++) { - coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); - lineAnalysis = coverageData[method]; - cells = lines[i].querySelectorAll('td'); - if (lineAnalysis === undefined) { - lineAnalysis = coverageData.AllTestMethods; - if (lineAnalysis.LVS !== 'gray') { - cells[0].setAttribute('class', 'red'); - cells[1].innerText = cells[1].textContent = '0'; - cells[4].setAttribute('class', 'lightred'); - } - } else { - cells[0].setAttribute('class', lineAnalysis.LVS); - cells[1].innerText = cells[1].textContent = lineAnalysis.VC; - cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); - } - } -}; - -var testMethods = document.getElementsByClassName('switchtestmethod'); -for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].addEventListener('change', switchTestMethod); -} - -/* Highlight test method by line */ -var toggleLine = function () { - if (selectedLine === this) { - selectedLine = null; - } else { - selectedLine = null; - unhighlightTestMethods(); - highlightTestMethods.call(this); - selectedLine = this; - } - -}; -var highlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var lineAnalysis; - var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); - var testMethods = document.getElementsByClassName('testmethod'); - - for (i = 0, l = testMethods.length; i < l; i++) { - lineAnalysis = coverageData[testMethods[i].id]; - if (lineAnalysis === undefined) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } else { - testMethods[i].className += ' light' + lineAnalysis.LVS; - } - } -}; -var unhighlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var testMethods = document.getElementsByClassName('testmethod'); - for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } -}; -var coverableLines = document.getElementsByClassName('coverableline'); -for (i = 0, l = coverableLines.length; i < l; i++) { - coverableLines[i].addEventListener('click', toggleLine); - coverableLines[i].addEventListener('mouseenter', highlightTestMethods); - coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); -} - -/* History charts */ -var renderChart = function (chart) { - // Remove current children (e.g. PNG placeholder) - while (chart.firstChild) { - chart.firstChild.remove(); - } - - var chartData = window[chart.getAttribute('data-data')]; - var options = { - axisY: { - type: undefined, - onlyInteger: true - }, - lineSmooth: false, - low: 0, - high: 100, - scaleMinSpace: 20, - onlyInteger: true, - fullWidth: true - }; - var lineChart = new Chartist.Line(chart, { - labels: [], - series: chartData.series - }, options); - - /* Zoom */ - var zoomButtonDiv = document.createElement("div"); - zoomButtonDiv.className = "toggleZoom"; - var zoomButtonLink = document.createElement("a"); - zoomButtonLink.setAttribute("href", ""); - var zoomButtonText = document.createElement("i"); - zoomButtonText.className = "icon-search-plus"; - - zoomButtonLink.appendChild(zoomButtonText); - zoomButtonDiv.appendChild(zoomButtonLink); - - chart.appendChild(zoomButtonDiv); - - zoomButtonDiv.addEventListener('click', function (event) { - event.preventDefault(); - - if (options.axisY.type === undefined) { - options.axisY.type = Chartist.AutoScaleAxis; - zoomButtonText.className = "icon-search-minus"; - } else { - options.axisY.type = undefined; - zoomButtonText.className = "icon-search-plus"; - } - - lineChart.update(null, options); - }); - - var tooltip = document.createElement("div"); - tooltip.className = "tooltip"; - - chart.appendChild(tooltip); - - /* Tooltips */ - var showToolTip = function () { - var point = this; - var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); - - tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; - tooltip.style.display = 'block'; - }; - - var moveToolTip = function (event) { - var box = chart.getBoundingClientRect(); - var left = event.pageX - box.left - window.pageXOffset; - var top = event.pageY - box.top - window.pageYOffset; - - tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; - tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; - }; - - var hideToolTip = function () { - tooltip.style.display = 'none'; - }; - - chart.addEventListener('mousemove', moveToolTip); - - lineChart.on('created', function () { - var chartPoints = chart.getElementsByClassName('ct-point'); - for (i = 0, l = chartPoints.length; i < l; i++) { - chartPoints[i].addEventListener('mousemove', showToolTip); - chartPoints[i].addEventListener('mouseout', hideToolTip); - } - }); -}; - -var charts = document.getElementsByClassName('historychart'); -for (i = 0, l = charts.length; i < l; i++) { - renderChart(charts[i]); -} \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml deleted file mode 100644 index dbfc7f84..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml +++ /dev/null @@ -1,1270 +0,0 @@ - - - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll - 2021-02-09T23:13:42Z - mscorlib - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe - 2021-03-10T01:18:39.6988681Z - vstest.console - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Client.dll - 2021-03-10T01:18:39.3608699Z - Microsoft.VisualStudio.TestPlatform.Client - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Common.dll - 2021-03-10T01:18:39.3648679Z - Microsoft.VisualStudio.TestPlatform.Common - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.Utilities.dll - 2021-03-10T01:18:39.1838668Z - Microsoft.TestPlatform.Utilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\NuGet.Frameworks.dll - 2021-03-10T01:18:39.455872Z - NuGet.Frameworks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.Extensions.FileSystemGlobbing.dll - 2021-03-10T01:18:39.1398684Z - Microsoft.Extensions.FileSystemGlobbing - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CrossPlatEngine.dll - 2021-03-10T01:18:39.1798685Z - Microsoft.TestPlatform.CrossPlatEngine - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.Cci.dll - 2021-03-10T01:18:40.1568669Z - Microsoft.Cci - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.InteropServices\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.InteropServices.dll - 2019-12-07T09:10:35.9300981Z - System.Runtime.InteropServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.BlameDataCollector.dll - 2021-03-10T01:18:38.7708669Z - Microsoft.TestPlatform.Extensions.BlameDataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.EventLogCollector.dll - 2021-03-10T01:18:38.7728666Z - Microsoft.TestPlatform.Extensions.EventLogCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.TestHostRuntimeProvider.dll - 2021-03-10T01:18:38.7748692Z - Microsoft.TestPlatform.TestHostRuntimeProvider - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.ArchitectureTools.PEReader.dll - 2021-03-10T01:18:39.2018677Z - Microsoft.VisualStudio.ArchitectureTools.PEReader - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Coverage.Interprocess.dll - 2021-03-10T01:18:38.7888677Z - Microsoft.VisualStudio.Coverage.Interprocess - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\netstandard\v4.0_2.0.0.0__cc7b13ffcd2ddd51\netstandard.dll - 2019-12-07T09:10:37.6330785Z - netstandard - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ValueTuple\v4.0_4.0.0.0__cc7b13ffcd2ddd51\System.ValueTuple.dll - 2019-12-07T09:10:37.742541Z - System.ValueTuple - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Fakes.DataCollector.dll - 2021-03-10T01:18:40.1588666Z - Microsoft.VisualStudio.Fakes.DataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter.dll - 2021-03-10T01:18:38.7898664Z - Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter.dll - 2021-03-10T01:18:38.7968662Z - Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Common.dll - 2021-03-10T01:18:39.2388684Z - Microsoft.VisualStudio.QualityTools.Common - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter.dll - 2021-03-10T01:18:38.7918661Z - Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger.dll - 2021-03-10T01:18:38.792866Z - Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter.dll - 2021-03-10T01:18:38.7948677Z - Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger.dll - 2021-03-10T01:18:38.7998656Z - Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration.dll - 2021-03-10T01:18:38.8038671Z - Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll - 2021-03-10T01:18:39.3488657Z - Microsoft.VisualStudio.QualityTools.UnitTestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter.dll - 2021-03-10T01:18:38.8048665Z - Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces.dll - 2021-03-10T01:18:38.8068659Z - Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension.dll - 2021-03-10T01:18:38.8078663Z - Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model.dll - 2021-03-10T01:18:38.8098657Z - Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector.dll - 2021-03-10T01:18:38.8118659Z - Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TraceDataCollector.dll - 2021-03-10T01:18:38.8168685Z - Microsoft.VisualStudio.TraceDataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.IntelliTrace.Core.dll - 2021-03-10T01:18:39.1678662Z - Microsoft.IntelliTrace.Core - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Reflection.Metadata.dll - 2021-03-10T01:18:39.5908676Z - System.Reflection.Metadata - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Collections.Immutable.dll - 2021-03-10T01:18:39.5778692Z - System.Collections.Immutable - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.FileSystem\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.FileSystem.dll - 2019-12-07T09:10:36.0095245Z - System.IO.FileSystem - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.MemoryMappedFiles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.MemoryMappedFiles.dll - 2019-12-07T09:10:34.5552678Z - System.IO.MemoryMappedFiles - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Handles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Handles.dll - 2019-12-07T09:10:37.6643716Z - System.Runtime.Handles - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.dll - 2019-12-07T09:10:36.0705812Z - System.Text.Encoding - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.Extensions.dll - 2019-12-07T09:10:36.1495949Z - System.Text.Encoding.Extensions - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Fakes.dll - 2021-03-10T01:18:40.1608669Z - Microsoft.VisualStudio.TestPlatform.Fakes - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CommunicationUtilities.dll - 2021-03-10T01:18:39.1748663Z - Microsoft.TestPlatform.CommunicationUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Newtonsoft.Json.dll - 2021-03-10T01:18:39.451869Z - Newtonsoft.Json - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll - 2019-12-07T09:10:37.6960843Z - System.Resources.ResourceManager - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ComponentModel.Composition\v4.0_4.0.0.0__b77a5c561934e089\System.ComponentModel.Composition.dll - 2019-12-07T09:10:36.1023351Z - System.ComponentModel.Composition - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.ReaderWriter\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.ReaderWriter.dll - 2019-12-07T09:10:34.5705678Z - System.Xml.ReaderWriter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.XDocument\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.XDocument.dll - 2019-12-07T09:10:34.5552678Z - System.Xml.XDocument - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll - 2019-12-07T09:10:35.962301Z - System.Xml.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\UnitTestProject1.dll - 2019-04-19T07:50:12.1584913Z - UnitTestProject1 - - - - - - - <Module> - - - - - UnitTestProject1.UnitTest1 - - - - 100663297 - System.Void UnitTestProject1.UnitTest1::TestMethod1() - - - - - - - - - - - - - - 100663298 - System.Void UnitTestProject1.UnitTest1::TestMethod2() - - - - - - - - - - - - - - 100663299 - System.Void UnitTestProject1.UnitTest1::.ctor() - - - - - - - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll - 2020-08-05T02:39:55.9298663Z - System.Runtime.Serialization - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\bin\debug\UnitTestProject2.dll - 2019-04-19T07:50:12.1584913Z - UnitTestProject2 - - - - - - - <Module> - - - - - UnitTestProject2.UnitTest1 - - - - 100663297 - System.Void UnitTestProject2.UnitTest1::TestMethod1() - - - - - - - - - - - - 100663298 - System.Void UnitTestProject2.UnitTest1::.ctor() - - - - - - - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll - 2020-08-05T02:39:55.9298663Z - System.Runtime.Serialization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections.Concurrent\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.Concurrent.dll - 2019-12-07T09:10:34.5552678Z - System.Collections.Concurrent - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\ClassLibrary1.dll - 2019-04-19T06:37:47.6105603Z - ClassLibrary1 - - - - - - - <Module> - - - - - ClassLibrary1.Calculation - - - - 100663297 - System.Int32 ClassLibrary1.Calculation::Add(System.Int32,System.Int32) - - - - - - - - - - - - 100663298 - System.Int32 ClassLibrary1.Calculation::Sub(System.Int32,System.Int32) - - - - - - - - - - - - 100663299 - System.Int32 ClassLibrary1.Calculation::Sub1(System.Int32,System.Int32) - - - - - - - - - - - - 100663300 - System.Void ClassLibrary1.Calculation::.ctor() - - - - - - - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll - 2019-12-07T09:10:37.6960843Z - System.Resources.ResourceManager - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.RegularExpressions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.RegularExpressions.dll - 2019-12-07T09:10:34.4610177Z - System.Text.RegularExpressions - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.ExecutionCommon.dll - 2021-03-10T01:18:39.2588672Z - Microsoft.VisualStudio.QualityTools.ExecutionCommon - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Resource.dll - 2021-03-10T01:18:39.3078688Z - Microsoft.VisualStudio.QualityTools.Resource - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.WebTestFramework.dll - 2021-03-10T01:18:39.3588667Z - Microsoft.VisualStudio.QualityTools.WebTestFramework - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg deleted file mode 100644 index 11b5cabf..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg deleted file mode 100644 index d11cf041..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg deleted file mode 100644 index f0148b3a..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg deleted file mode 100644 index 252166bb..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg deleted file mode 100644 index 3c30c365..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg deleted file mode 100644 index 79327232..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg deleted file mode 100644 index c174eb5e..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg deleted file mode 100644 index 04b24ecc..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg deleted file mode 100644 index 567c11f3..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg deleted file mode 100644 index bb225544..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg deleted file mode 100644 index 0e9a8601..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm deleted file mode 100644 index f2f23f7e..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - -Summary - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - - - -
Generated on:2021/3/26 - 下午 04:34:49
Parser:OpenCoverParser
Assemblies:3
Classes:3
Files:3
Covered lines:15
Uncovered lines:7
Coverable lines:22
Total lines:59
Line coverage:68.1% (15 of 22)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Risk Hotspots

- - -

No risk hotspots found.

-

Coverage

- - ----------- - - - - - - - - - -
NameCoveredUncoveredCoverableTotalLine coverageBranch coverage
ClassLibrary13691833.3%
  
 
ClassLibrary1.Calculation3691833.3%
  
 
UnitTestProject191102690%
  
 
UnitTestProject1.UnitTest191102690%
  
 
UnitTestProject230315100%
 
 
UnitTestProject2.UnitTest130315100%
 
 
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js deleted file mode 100644 index 160252eb..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js +++ /dev/null @@ -1,274 +0,0 @@ -/* Chartist.js 0.11.0 - * Copyright © 2017 Gion Kunz - * Free to use under either the WTFPL license or the MIT license. - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT - */ - -!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { - var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { - "use strict"; function d(a) { - var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { - var i = c.serialMap(b.normalized.series, function () { - return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) - }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") - } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) - } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) - }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a -}); - -var i, l, selectedLine = null; - -/* Navigate to hash without browser history entry */ -var navigateToHash = function () { - if (window.history !== undefined && window.history.replaceState !== undefined) { - window.history.replaceState(undefined, undefined, this.getAttribute("href")); - } -}; - -var hashLinks = document.getElementsByClassName('navigatetohash'); -for (i = 0, l = hashLinks.length; i < l; i++) { - hashLinks[i].addEventListener('click', navigateToHash); -} - -/* Switch test method */ -var switchTestMethod = function () { - var method = this.getAttribute("value"); - console.log("Selected test method: " + method); - - var lines, i, l, coverageData, lineAnalysis, cells; - - lines = document.querySelectorAll('.lineAnalysis tr'); - - for (i = 1, l = lines.length; i < l; i++) { - coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); - lineAnalysis = coverageData[method]; - cells = lines[i].querySelectorAll('td'); - if (lineAnalysis === undefined) { - lineAnalysis = coverageData.AllTestMethods; - if (lineAnalysis.LVS !== 'gray') { - cells[0].setAttribute('class', 'red'); - cells[1].innerText = cells[1].textContent = '0'; - cells[4].setAttribute('class', 'lightred'); - } - } else { - cells[0].setAttribute('class', lineAnalysis.LVS); - cells[1].innerText = cells[1].textContent = lineAnalysis.VC; - cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); - } - } -}; - -var testMethods = document.getElementsByClassName('switchtestmethod'); -for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].addEventListener('change', switchTestMethod); -} - -/* Highlight test method by line */ -var toggleLine = function () { - if (selectedLine === this) { - selectedLine = null; - } else { - selectedLine = null; - unhighlightTestMethods(); - highlightTestMethods.call(this); - selectedLine = this; - } - -}; -var highlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var lineAnalysis; - var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); - var testMethods = document.getElementsByClassName('testmethod'); - - for (i = 0, l = testMethods.length; i < l; i++) { - lineAnalysis = coverageData[testMethods[i].id]; - if (lineAnalysis === undefined) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } else { - testMethods[i].className += ' light' + lineAnalysis.LVS; - } - } -}; -var unhighlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var testMethods = document.getElementsByClassName('testmethod'); - for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } -}; -var coverableLines = document.getElementsByClassName('coverableline'); -for (i = 0, l = coverableLines.length; i < l; i++) { - coverableLines[i].addEventListener('click', toggleLine); - coverableLines[i].addEventListener('mouseenter', highlightTestMethods); - coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); -} - -/* History charts */ -var renderChart = function (chart) { - // Remove current children (e.g. PNG placeholder) - while (chart.firstChild) { - chart.firstChild.remove(); - } - - var chartData = window[chart.getAttribute('data-data')]; - var options = { - axisY: { - type: undefined, - onlyInteger: true - }, - lineSmooth: false, - low: 0, - high: 100, - scaleMinSpace: 20, - onlyInteger: true, - fullWidth: true - }; - var lineChart = new Chartist.Line(chart, { - labels: [], - series: chartData.series - }, options); - - /* Zoom */ - var zoomButtonDiv = document.createElement("div"); - zoomButtonDiv.className = "toggleZoom"; - var zoomButtonLink = document.createElement("a"); - zoomButtonLink.setAttribute("href", ""); - var zoomButtonText = document.createElement("i"); - zoomButtonText.className = "icon-search-plus"; - - zoomButtonLink.appendChild(zoomButtonText); - zoomButtonDiv.appendChild(zoomButtonLink); - - chart.appendChild(zoomButtonDiv); - - zoomButtonDiv.addEventListener('click', function (event) { - event.preventDefault(); - - if (options.axisY.type === undefined) { - options.axisY.type = Chartist.AutoScaleAxis; - zoomButtonText.className = "icon-search-minus"; - } else { - options.axisY.type = undefined; - zoomButtonText.className = "icon-search-plus"; - } - - lineChart.update(null, options); - }); - - var tooltip = document.createElement("div"); - tooltip.className = "tooltip"; - - chart.appendChild(tooltip); - - /* Tooltips */ - var showToolTip = function () { - var point = this; - var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); - - tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; - tooltip.style.display = 'block'; - }; - - var moveToolTip = function (event) { - var box = chart.getBoundingClientRect(); - var left = event.pageX - box.left - window.pageXOffset; - var top = event.pageY - box.top - window.pageYOffset; - - tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; - tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; - }; - - var hideToolTip = function () { - tooltip.style.display = 'none'; - }; - - chart.addEventListener('mousemove', moveToolTip); - - lineChart.on('created', function () { - var chartPoints = chart.getElementsByClassName('ct-point'); - for (i = 0, l = chartPoints.length; i < l; i++) { - chartPoints[i].addEventListener('mousemove', showToolTip); - chartPoints[i].addEventListener('mouseout', hideToolTip); - } - }); -}; - -var charts = document.getElementsByClassName('historychart'); -for (i = 0, l = charts.length; i < l; i++) { - renderChart(charts[i]); -} - -var assemblies = [ - { - "name": "ClassLibrary1", - "classes": [ - { "name": "ClassLibrary1.Calculation", "rp": "ClassLibrary1_Calculation.htm", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [33.3], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "lcq": 33.3, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, - { - "name": "UnitTestProject1", - "classes": [ - { "name": "UnitTestProject1.UnitTest1", "rp": "UnitTestProject1_UnitTest1.htm", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [90], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "lcq": 90, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, - { - "name": "UnitTestProject2", - "classes": [ - { "name": "UnitTestProject2.UnitTest1", "rp": "UnitTestProject2_UnitTest1.htm", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [100], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "lcq": 100, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, -]; - -var historicCoverageExecutionTimes = []; - -var riskHotspotMetrics = [ -]; - -var riskHotspots = [ -]; - -var branchCoverageAvailable = true; - - -var translations = { -'top': 'Top:', -'all': 'All', -'assembly': 'Assembly', -'class': 'Class', -'method': 'Method', -'lineCoverage': 'LineCoverage', -'noGrouping': 'No grouping', -'byAssembly': 'By assembly', -'byNamespace': 'By namespace, Level:', -'all': 'All', -'collapseAll': 'Collapse all', -'expandAll': 'Expand all', -'grouping': 'Grouping:', -'filter': 'Filter:', -'name': 'Name', -'covered': 'Covered', -'uncovered': 'Uncovered', -'coverable': 'Coverable', -'total': 'Total', -'coverage': 'Line coverage', -'branchCoverage': 'Branch coverage', -'history': 'Coverage History', -'compareHistory': 'Compare with:', -'date': 'Date', -'allChanges': 'All changes', -'lineCoverageIncreaseOnly': 'Line coverage: Increase only', -'lineCoverageDecreaseOnly': 'Line coverage: Decrease only', -'branchCoverageIncreaseOnly': 'Branch coverage: Increase only', -'branchCoverageDecreaseOnly': 'Branch coverage: Decrease only' -}; - - -!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c",this._properties=n&&n.properties||{},this._zoneDelegate=new c(this,this._parent&&this._parent._zoneDelegate,n)}return n.assertZonePatched=function(){if(t.Promise!==T.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")},Object.defineProperty(n,"root",{get:function(){for(var t=n.current;t.parent;)t=t.parent;return t},enumerable:!0,configurable:!0}),Object.defineProperty(n,"current",{get:function(){return j.zone},enumerable:!0,configurable:!0}),Object.defineProperty(n,"currentTask",{get:function(){return M},enumerable:!0,configurable:!0}),n.__load_patch=function(o,i){if(T.hasOwnProperty(o))throw Error("Already loaded patch: "+o);if(!t["__Zone_disable_"+o]){var u="Zone:"+o;r(u),T[o]=i(t,n,O),e(u,u)}},Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"name",{get:function(){return this._name},enumerable:!0,configurable:!0}),n.prototype.get=function(t){var n=this.getZoneWith(t);if(n)return n._properties[t]},n.prototype.getZoneWith=function(t){for(var n=this;n;){if(n._properties.hasOwnProperty(t))return n;n=n._parent}return null},n.prototype.fork=function(t){if(!t)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,t)},n.prototype.wrap=function(t,n){if("function"!=typeof t)throw new Error("Expecting function got: "+t);var r=this._zoneDelegate.intercept(this,t,n),e=this;return function(){return e.runGuarded(r,this,arguments,n)}},n.prototype.run=function(t,n,r,e){void 0===n&&(n=void 0),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{return this._zoneDelegate.invoke(this,t,n,r,e)}finally{j=j.parent}},n.prototype.runGuarded=function(t,n,r,e){void 0===n&&(n=null),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{try{return this._zoneDelegate.invoke(this,t,n,r,e)}catch(o){if(this._zoneDelegate.handleError(this,o))throw o}}finally{j=j.parent}},n.prototype.runTask=function(t,n,r){if(t.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");if(t.state!==g||t.type!==F){var e=t.state!=_;e&&t._transitionTo(_,m),t.runCount++;var o=M;M=t,j={parent:j,zone:this};try{t.type==S&&t.data&&!t.data.isPeriodic&&(t.cancelFn=null);try{return this._zoneDelegate.invokeTask(this,t,n,r)}catch(i){if(this._zoneDelegate.handleError(this,i))throw i}}finally{t.state!==g&&t.state!==w&&(t.type==F||t.data&&t.data.isPeriodic?e&&t._transitionTo(m,_):(t.runCount=0,this._updateTaskCount(t,-1),e&&t._transitionTo(g,_,g))),j=j.parent,M=o}}},n.prototype.scheduleTask=function(t){if(t.zone&&t.zone!==this)for(var n=this;n;){if(n===t.zone)throw Error("can not reschedule task to "+this.name+" which is descendants of the original zone "+t.zone.name);n=n.parent}t._transitionTo(b,g);var r=[];t._zoneDelegates=r,t._zone=this;try{t=this._zoneDelegate.scheduleTask(this,t)}catch(e){throw t._transitionTo(w,b,g),this._zoneDelegate.handleError(this,e),e}return t._zoneDelegates===r&&this._updateTaskCount(t,1),t.state==b&&t._transitionTo(m,b),t},n.prototype.scheduleMicroTask=function(t,n,r,e){return this.scheduleTask(new a(x,t,n,r,e,null))},n.prototype.scheduleMacroTask=function(t,n,r,e,o){return this.scheduleTask(new a(S,t,n,r,e,o))},n.prototype.scheduleEventTask=function(t,n,r,e,o){return this.scheduleTask(new a(F,t,n,r,e,o))},n.prototype.cancelTask=function(t){if(t.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");t._transitionTo(k,m,_);try{this._zoneDelegate.cancelTask(this,t)}catch(n){throw t._transitionTo(w,k),this._zoneDelegate.handleError(this,n),n}return this._updateTaskCount(t,-1),t._transitionTo(g,k),t.runCount=0,t},n.prototype._updateTaskCount=function(t,n){var r=t._zoneDelegates;-1==n&&(t._zoneDelegates=null);for(var e=0;e0,macroTask:r.macroTask>0,eventTask:r.eventTask>0,change:t})},t}(),a=function(){function n(r,e,o,i,u,c){this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=r,this.source=e,this.data=i,this.scheduleFn=u,this.cancelFn=c,this.callback=o;var a=this;this.invoke=r===F&&i&&i.useG?n.invokeTask:function(){return n.invokeTask.call(t,a,this,arguments)}}return n.invokeTask=function(t,n,r){t||(t=this),K++;try{return t.runCount++,t.zone.runTask(t,n,r)}finally{1==K&&y(),K--}},Object.defineProperty(n.prototype,"zone",{get:function(){return this._zone},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"state",{get:function(){return this._state},enumerable:!0,configurable:!0}),n.prototype.cancelScheduleRequest=function(){this._transitionTo(g,b)},n.prototype._transitionTo=function(t,n,r){if(this._state!==n&&this._state!==r)throw new Error(this.type+" '"+this.source+"': can not transition to '"+t+"', expecting state '"+n+"'"+(r?" or '"+r+"'":"")+", was '"+this._state+"'.");this._state=t,t==g&&(this._zoneDelegates=null)},n.prototype.toString=function(){return this.data&&void 0!==this.data.handleId?this.data.handleId:Object.prototype.toString.call(this)},n.prototype.toJSON=function(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}},n}(),f=X("setTimeout"),s=X("Promise"),l=X("then"),h=[],p=!1;function v(n){0===K&&0===h.length&&(o||t[s]&&(o=t[s].resolve(0)),o?o[l](y):t[f](y,0)),n&&h.push(n)}function y(){if(!p){for(p=!0;h.length;){var t=h;h=[];for(var n=0;n=0;r--)"function"==typeof t[r]&&(t[r]=h(t[r],n+"_"+r));return t}function k(t){return!t||!1!==t.writable&&!("function"==typeof t.get&&void 0===t.set)}var w="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,x=!("nw"in g)&&void 0!==g.process&&"[object process]"==={}.toString.call(g.process),S=!x&&!w&&!(!y||!d.HTMLElement),F=void 0!==g.process&&"[object process]"==={}.toString.call(g.process)&&!w&&!(!y||!d.HTMLElement),T={},O=function(t){if(t=t||g.event){var n=T[t.type];n||(n=T[t.type]=v("ON_PROPERTY"+t.type));var r=(this||t.target||g)[n],e=r&&r.apply(this,arguments);return null==e||e||t.preventDefault(),e}};function j(r,e,o){var i=t(r,e);if(!i&&o&&t(o,e)&&(i={enumerable:!0,configurable:!0}),i&&i.configurable){delete i.writable,delete i.value;var u=i.get,c=i.set,a=e.substr(2),f=T[a];f||(f=T[a]=v("ON_PROPERTY"+a)),i.set=function(t){var n=this;n||r!==g||(n=g),n&&(n[f]&&n.removeEventListener(a,O),c&&c.apply(n,m),"function"==typeof t?(n[f]=t,n.addEventListener(a,O,!1)):n[f]=null)},i.get=function(){var t=this;if(t||r!==g||(t=g),!t)return null;var n=t[f];if(n)return n;if(u){var o=u&&u.call(this);if(o)return i.set.call(this,o),"function"==typeof t[b]&&t.removeAttribute(e),o}return null},n(r,e,i)}}function M(t,n,r){if(n)for(var e=0;e1?new c(n,r):new c(n),l=t(s,"onmessage");return l&&!1===l.configurable?(a=e(s),f=s,[i,u,"send","close"].forEach(function(t){a[t]=function(){var n=o.call(arguments);if(t===i||t===u){var r=n.length>0?n[0]:void 0;if(r){var e=Zone.__symbol__("ON_PROPERTY"+r);s[e]=a[e]}}return s[t].apply(s,n)}})):a=s,M(a,["close","error","message","open"],f),a};var a=r.WebSocket;for(var f in c)a[f]=c[f]}(0,a)}}var lt=v("unbound");Zone.__load_patch("util",function(t,n,r){r.patchOnProperties=M,r.patchMethod=X,r.bindArguments=_}),Zone.__load_patch("timers",function(t){G(t,"set","clear","Timeout"),G(t,"set","clear","Interval"),G(t,"set","clear","Immediate")}),Zone.__load_patch("requestAnimationFrame",function(t){G(t,"request","cancel","AnimationFrame"),G(t,"mozRequest","mozCancel","AnimationFrame"),G(t,"webkitRequest","webkitCancel","AnimationFrame")}),Zone.__load_patch("blocking",function(t,n){for(var r=["alert","prompt","confirm"],e=0;e=0&&"function"==typeof r[e.cbIdx]?p(e.name,r[e.cbIdx],e,i,null):t.apply(n,r)}})}()}),Zone.__load_patch("XHR",function(t,n){!function(n){var f=XMLHttpRequest.prototype,s=f[c],l=f[a];if(!s){var h=t.XMLHttpRequestEventTarget;if(h){var v=h.prototype;s=v[c],l=v[a]}}var y="readystatechange",d="scheduled";function g(t){XMLHttpRequest[i]=!1;var n=t.data,e=n.target,u=e[o];s||(s=e[c],l=e[a]),u&&l.call(e,y,u);var f=e[o]=function(){e.readyState===e.DONE&&!n.aborted&&XMLHttpRequest[i]&&t.state===d&&t.invoke()};return s.call(e,y,f),e[r]||(e[r]=t),k.apply(e,n.args),XMLHttpRequest[i]=!0,t}function b(){}function m(t){var n=t.data;return n.aborted=!0,w.apply(n.target,n.args)}var _=X(f,"open",function(){return function(t,n){return t[e]=0==n[2],t[u]=n[1],_.apply(t,n)}}),k=X(f,"send",function(){return function(t,n){return t[e]?k.apply(t,n):p("XMLHttpRequest.send",b,{target:t,url:t[u],isPeriodic:!1,delay:null,args:n,aborted:!1},g,m)}}),w=X(f,"abort",function(){return function(t){var n=t[r];if(n&&"string"==typeof n.type){if(null==n.cancelFn||n.data&&n.data.aborted)return;n.zone.cancelTask(n)}}})}();var r=v("xhrTask"),e=v("xhrSync"),o=v("xhrListener"),i=v("xhrScheduled"),u=v("xhrURL")}),Zone.__load_patch("geolocation",function(n){n.navigator&&n.navigator.geolocation&&function(n,r){for(var e=n.constructor.name,o=function(o){var i=r[o],u=n[i];if(u){if(!k(t(n,i)))return"continue";n[i]=function(t){var n=function(){return t.apply(this,_(arguments,e+"."+i))};return Z(n,t),n}(u)}},i=0;if;)a.call(t,u=c[f++])&&n.push(u);return n}},"1TsA":function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},"1sa7":function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},"25dN":function(t,n,r){var e=r("XKFU");e(e.S,"Object",{is:r("g6HL")})},"2OiF":function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"2Spj":function(t,n,r){var e=r("XKFU");e(e.P,"Function",{bind:r("8MEG")})},"2atp":function(t,n,r){var e=r("XKFU"),o=Math.atanh;e(e.S+e.F*!(o&&1/o(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},"3Lyj":function(t,n,r){var e=r("KroJ");t.exports=function(t,n,r){for(var o in n)e(t,o,n[o],r);return t}},"4A4+":function(t,n,r){r("2Spj"),r("f3/d"),r("IXt9"),t.exports=r("g3g5").Function},"4LiD":function(t,n,r){"use strict";var e=r("dyZX"),o=r("XKFU"),i=r("KroJ"),u=r("3Lyj"),c=r("Z6vF"),a=r("SlkY"),f=r("9gX7"),s=r("0/R4"),l=r("eeVq"),h=r("XMVh"),p=r("fyDq"),v=r("Xbzi");t.exports=function(t,n,r,y,d,g){var b=e[t],m=b,_=d?"set":"add",k=m&&m.prototype,w={},x=function(t){var n=k[t];i(k,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||k.forEach&&!l(function(){(new m).entries().next()}))){var S=new m,F=S[_](g?{}:-0,1)!=S,T=l(function(){S.has(1)}),O=h(function(t){new m(t)}),j=!g&&l(function(){for(var t=new m,n=5;n--;)t[_](n,n);return!t.has(-0)});O||((m=n(function(n,r){f(n,m,t);var e=v(new b,n,m);return null!=r&&a(r,d,e[_],e),e})).prototype=k,k.constructor=m),(T||j)&&(x("delete"),x("has"),d&&x("get")),(j||F)&&x(_),g&&k.clear&&delete k.clear}else m=y.getConstructor(n,t,d,_),u(m.prototype,r),c.NEED=!0;return p(m,t),w[t]=m,o(o.G+o.W+o.F*(m!=b),w),g||y.setStrong(m,t,d),m}},"4R4u":function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"5Pf0":function(t,n,r){var e=r("S/j/"),o=r("OP3Y");r("Xtr8")("getPrototypeOf",function(){return function(t){return o(e(t))}})},"69bn":function(t,n,r){var e=r("y3w9"),o=r("2OiF"),i=r("K0xU")("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||null==(r=e(u)[i])?n:o(r)}},"6AQ9":function(t,n,r){"use strict";var e=r("XKFU"),o=r("8a7r");e(e.S+e.F*r("eeVq")(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)o(r,t,arguments[t++]);return r.length=n,r}})},"6FMO":function(t,n,r){var e=r("0/R4"),o=r("EWmC"),i=r("K0xU")("species");t.exports=function(t){var n;return o(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!o(n.prototype)||(n=void 0),e(n)&&null===(n=n[i])&&(n=void 0)),void 0===n?Array:n}},"7h0T":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isNaN:function(t){return t!=t}})},"8+KV":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(0),i=r("LyE8")([].forEach,!0);e(e.P+e.F*!i,"Array",{forEach:function(t){return o(this,t,arguments[1])}})},"84bF":function(t,n,r){"use strict";r("OGtf")("small",function(t){return function(){return t(this,"small","","")}})},"8MEG":function(t,n,r){"use strict";var e=r("2OiF"),o=r("0/R4"),i=r("MfQN"),u=[].slice,c={};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),a=function(){var e=r.concat(u.call(arguments));return this instanceof a?function(t,n,r){if(!(n in c)){for(var e=[],o=0;o0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(o(this,"Map"),t);return n&&n.v},set:function(t,n){return e.def(o(this,"Map"),0===t?0:t,n)}},e,!0)},"9P93":function(t,n,r){var e=r("XKFU"),o=Math.imul;e(e.S+e.F*r("eeVq")(function(){return-5!=o(4294967295,5)||2!=o.length}),"Math",{imul:function(t,n){var r=+t,e=+n,o=65535&r,i=65535&e;return 0|o*i+((65535&r>>>16)*i+o*(65535&e>>>16)<<16>>>0)}})},"9VmF":function(t,n,r){"use strict";var e=r("XKFU"),o=r("ne8i"),i=r("0sh+"),u="".startsWith;e(e.P+e.F*r("UUeW")("startsWith"),"String",{startsWith:function(t){var n=i(this,t,"startsWith"),r=o(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return u?u.call(n,e,r):n.slice(r,r+e.length)===e}})},"9gX7":function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},A2zW:function(t,n,r){"use strict";var e=r("XKFU"),o=r("RYi7"),i=r("vvmO"),u=r("l0Rn"),c=1..toFixed,a=Math.floor,f=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l=function(t,n){for(var r=-1,e=n;++r<6;)f[r]=(e+=t*f[r])%1e7,e=a(e/1e7)},h=function(t){for(var n=6,r=0;--n>=0;)f[n]=a((r+=f[n])/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==f[t]){var r=String(f[t]);n=""===n?r:n+u.call("0",7-r.length)+r}return n},v=function(t,n,r){return 0===n?r:n%2==1?v(t,n-1,r*t):v(t*t,n/2,r)};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r("eeVq")(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,a=i(this,s),f=o(t),y="",d="0";if(f<0||f>20)throw RangeError(s);if(a!=a)return"NaN";if(a<=-1e21||a>=1e21)return String(a);if(a<0&&(y="-",a=-a),a>1e-21)if(r=(n=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n}(a*v(2,69,1))-69)<0?a*v(2,-n,1):a/v(2,n,1),r*=4503599627370496,(n=52-n)>0){for(l(0,r),e=f;e>=7;)l(1e7,0),e-=7;for(l(v(10,e,1),0),e=n-1;e>=23;)h(1<<23),e-=23;h(1<0?y+((c=d.length)<=f?"0."+u.call("0",f-c)+d:d.slice(0,c-f)+"."+d.slice(c-f)):y+d}})},A5AN:function(t,n,r){"use strict";var e=r("AvRE")(!0);t.exports=function(t,n,r){return n+(r?e(t,n).length:1)}},Afnz:function(t,n,r){"use strict";var e=r("LQAc"),o=r("XKFU"),i=r("KroJ"),u=r("Mukb"),c=r("hPIQ"),a=r("QaDb"),f=r("fyDq"),s=r("OP3Y"),l=r("K0xU")("iterator"),h=!([].keys&&"next"in[].keys()),p=function(){return this};t.exports=function(t,n,r,v,y,d,g){a(r,n,v);var b,m,_,k=function(t){if(!h&&t in F)return F[t];switch(t){case"keys":case"values":return function(){return new r(this,t)}}return function(){return new r(this,t)}},w=n+" Iterator",x="values"==y,S=!1,F=t.prototype,T=F[l]||F["@@iterator"]||y&&F[y],O=T||k(y),j=y?x?k("entries"):O:void 0,M="Array"==n&&F.entries||T;if(M&&(_=s(M.call(new t)))!==Object.prototype&&_.next&&(f(_,w,!0),e||"function"==typeof _[l]||u(_,l,p)),x&&T&&"values"!==T.name&&(S=!0,O=function(){return T.call(this)}),e&&!g||!h&&!S&&F[l]||u(F,l,O),c[n]=O,c[w]=p,y)if(b={values:x?O:k("values"),keys:d?O:k("keys"),entries:j},g)for(m in b)m in F||i(F,m,b[m]);else o(o.P+o.F*(h||S),n,b);return b}},AphP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("S/j/"),i=r("apmT");e(e.P+e.F*r("eeVq")(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=o(this),r=i(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},AvRE:function(t,n,r){var e=r("RYi7"),o=r("vhPU");t.exports=function(t){return function(n,r){var i,u,c=String(o(n)),a=e(r),f=c.length;return a<0||a>=f?t?"":void 0:(i=c.charCodeAt(a))<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536}}},BC7C:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{fround:r("kcoS")})},"BJ/l":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log1p:r("1sa7")})},BP8U:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.S+e.F*(Number.parseInt!=o),"Number",{parseInt:o})},Btvt:function(t,n,r){"use strict";var e=r("I8a+"),o={};o[r("K0xU")("toStringTag")]="z",o+""!="[object z]"&&r("KroJ")(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},"C/va":function(t,n,r){"use strict";var e=r("y3w9");t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},CkkT:function(t,n,r){var e=r("m0Pp"),o=r("Ymqv"),i=r("S/j/"),u=r("ne8i"),c=r("zRwo");t.exports=function(t,n){var r=1==t,a=2==t,f=3==t,s=4==t,l=6==t,h=5==t||l,p=n||c;return function(n,c,v){for(var y,d,g=i(n),b=o(g),m=e(c,v,3),_=u(b.length),k=0,w=r?p(n,_):a?p(n,0):void 0;_>k;k++)if((h||k in b)&&(d=m(y=b[k],k,g),t))if(r)w[k]=d;else if(d)switch(t){case 3:return!0;case 5:return y;case 6:return k;case 2:w.push(y)}else if(s)return!1;return l?-1:f||s?s:w}}},CuTL:function(t,n,r){r("fyVe"),r("U2t9"),r("2atp"),r("+auO"),r("MtdB"),r("Jcmo"),r("nzyx"),r("BC7C"),r("x8ZO"),r("9P93"),r("eHKK"),r("BJ/l"),r("pp/T"),r("CyHz"),r("bBoP"),r("x8Yj"),r("hLT2"),t.exports=r("g3g5").Math},CyHz:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{sign:r("lvtm")})},DNiP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduce,!0),"Array",{reduce:function(t){return o(this,t,arguments.length,arguments[1],!1)}})},DVgA:function(t,n,r){var e=r("zhAb"),o=r("4R4u");t.exports=Object.keys||function(t){return e(t,o)}},DW2E:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("freeze",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},EK0E:function(t,n,r){"use strict";var e,o=r("CkkT")(0),i=r("KroJ"),u=r("Z6vF"),c=r("czNK"),a=r("ZD67"),f=r("0/R4"),s=r("eeVq"),l=r("s5qY"),h=u.getWeak,p=Object.isExtensible,v=a.ufstore,y={},d=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},g={get:function(t){if(f(t)){var n=h(t);return!0===n?v(l(this,"WeakMap")).get(t):n?n[this._i]:void 0}},set:function(t,n){return a.def(l(this,"WeakMap"),t,n)}},b=t.exports=r("4LiD")("WeakMap",d,g,a,!0,!0);s(function(){return 7!=(new b).set((Object.freeze||Object)(y),7).get(y)})&&(c((e=a.getConstructor(d,"WeakMap")).prototype,g),u.NEED=!0,o(["delete","has","get","set"],function(t){var n=b.prototype,r=n[t];i(n,t,function(n,o){if(f(n)&&!p(n)){this._f||(this._f=new e);var i=this._f[t](n,o);return"set"==t?this:i}return r.call(this,n,o)})}))},EWmC:function(t,n,r){var e=r("LZWt");t.exports=Array.isArray||function(t){return"Array"==e(t)}},EemH:function(t,n,r){var e=r("UqcF"),o=r("RjD/"),i=r("aCFj"),u=r("apmT"),c=r("aagx"),a=r("xpql"),f=Object.getOwnPropertyDescriptor;n.f=r("nh4g")?f:function(t,n){if(t=i(t),n=u(n,!0),a)try{return f(t,n)}catch(r){}if(c(t,n))return o(!e.f.call(t,n),t[n])}},FEjr:function(t,n,r){"use strict";r("OGtf")("strike",function(t){return function(){return t(this,"strike","","")}})},FJW5:function(t,n,r){var e=r("hswa"),o=r("y3w9"),i=r("DVgA");t.exports=r("nh4g")?Object.defineProperties:function(t,n){o(t);for(var r,u=i(n),c=u.length,a=0;c>a;)e.f(t,r=u[a++],n[r]);return t}},FLlr:function(t,n,r){var e=r("XKFU");e(e.P,"String",{repeat:r("l0Rn")})},FlsD:function(t,n,r){var e=r("0/R4");r("Xtr8")("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},GNAe:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.G+e.F*(parseInt!=o),{parseInt:o})},H6hf:function(t,n,r){var e=r("y3w9");t.exports=function(t,n,r,o){try{return o?n(e(r)[0],r[1]):n(r)}catch(u){var i=t.return;throw void 0!==i&&e(i.call(t)),u}}},"HAE/":function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperty:r("hswa").f})},HEwt:function(t,n,r){"use strict";var e=r("m0Pp"),o=r("XKFU"),i=r("S/j/"),u=r("H6hf"),c=r("M6Qj"),a=r("ne8i"),f=r("8a7r"),s=r("J+6e");o(o.S+o.F*!r("XMVh")(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,o,l,h=i(t),p="function"==typeof this?this:Array,v=arguments.length,y=v>1?arguments[1]:void 0,d=void 0!==y,g=0,b=s(h);if(d&&(y=e(y,v>2?arguments[2]:void 0,2)),null==b||p==Array&&c(b))for(r=new p(n=a(h.length));n>g;g++)f(r,g,d?y(h[g],g):h[g]);else for(l=b.call(h),r=new p;!(o=l.next()).done;g++)f(r,g,d?u(l,y,[o.value,g],!0):o.value);return r.length=g,r}})},I78e:function(t,n,r){"use strict";var e=r("XKFU"),o=r("+rLv"),i=r("LZWt"),u=r("d/Gc"),c=r("ne8i"),a=[].slice;e(e.P+e.F*r("eeVq")(function(){o&&a.call(o)}),"Array",{slice:function(t,n){var r=c(this.length),e=i(this);if(n=void 0===n?r:n,"Array"==e)return a.call(this,t,n);for(var o=u(t,r),f=u(n,r),s=c(f-o),l=new Array(s),h=0;h1?arguments[1]:void 0)}}),r("nGyu")(i)},"IU+Z":function(t,n,r){"use strict";r("sMXx");var e=r("KroJ"),o=r("Mukb"),i=r("eeVq"),u=r("vhPU"),c=r("K0xU"),a=r("Ugos"),f=c("species"),s=!i(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")}),l=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var r="ab".split(t);return 2===r.length&&"a"===r[0]&&"b"===r[1]}();t.exports=function(t,n,r){var h=c(t),p=!i(function(){var n={};return n[h]=function(){return 7},7!=""[t](n)}),v=p?!i(function(){var n=!1,r=/a/;return r.exec=function(){return n=!0,null},"split"===t&&(r.constructor={},r.constructor[f]=function(){return r}),r[h](""),!n}):void 0;if(!p||!v||"replace"===t&&!s||"split"===t&&!l){var y=/./[h],d=r(u,h,""[t],function(t,n,r,e,o){return n.exec===a?p&&!o?{done:!0,value:y.call(n,r,e)}:{done:!0,value:t.call(r,n,e)}:{done:!1}}),g=d[1];e(String.prototype,t,d[0]),o(RegExp.prototype,h,2==n?function(t,n){return g.call(t,this,n)}:function(t){return g.call(t,this)})}}},IXt9:function(t,n,r){"use strict";var e=r("0/R4"),o=r("OP3Y"),i=r("K0xU")("hasInstance"),u=Function.prototype;i in u||r("hswa").f(u,i,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=o(t);)if(this.prototype===t)return!0;return!1}})},Iw71:function(t,n,r){var e=r("0/R4"),o=r("dyZX").document,i=e(o)&&e(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"J+6e":function(t,n,r){var e=r("I8a+"),o=r("K0xU")("iterator"),i=r("hPIQ");t.exports=r("g3g5").getIteratorMethod=function(t){if(null!=t)return t[o]||t["@@iterator"]||i[e(t)]}},JCqj:function(t,n,r){"use strict";r("OGtf")("sup",function(t){return function(){return t(this,"sup","","")}})},Jcmo:function(t,n,r){var e=r("XKFU"),o=Math.exp;e(e.S,"Math",{cosh:function(t){return(o(t=+t)+o(-t))/2}})},JduL:function(t,n,r){r("Xtr8")("getOwnPropertyNames",function(){return r("e7yV").f})},JiEa:function(t,n){n.f=Object.getOwnPropertySymbols},K0xU:function(t,n,r){var e=r("VTer")("wks"),o=r("ylqs"),i=r("dyZX").Symbol,u="function"==typeof i;(t.exports=function(t){return e[t]||(e[t]=u&&i[t]||(u?i:o)("Symbol."+t))}).store=e},KKXr:function(t,n,r){"use strict";var e=r("quPj"),o=r("y3w9"),i=r("69bn"),u=r("A5AN"),c=r("ne8i"),a=r("Xxuz"),f=r("Ugos"),s=Math.min,l=[].push,h=!!function(){try{return new RegExp("x","y")}catch(t){}}();r("IU+Z")("split",2,function(t,n,r,p){var v;return v="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var o=String(this);if(void 0===t&&0===n)return[];if(!e(t))return r.call(o,t,n);for(var i,u,c,a=[],s=0,h=void 0===n?4294967295:n>>>0,p=new RegExp(t.source,(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":"")+"g");(i=f.call(p,o))&&!((u=p.lastIndex)>s&&(a.push(o.slice(s,i.index)),i.length>1&&i.index=h));)p.lastIndex===i.index&&p.lastIndex++;return s===o.length?!c&&p.test("")||a.push(""):a.push(o.slice(s)),a.length>h?a.slice(0,h):a}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:r.call(this,t,n)}:r,[function(r,e){var o=t(this),i=null==r?void 0:r[n];return void 0!==i?i.call(r,o,e):v.call(String(o),r,e)},function(t,n){var e=p(v,t,this,n,v!==r);if(e.done)return e.value;var f=o(t),l=String(this),y=i(f,RegExp),d=f.unicode,g=new y(h?f:"^(?:"+f.source+")",(f.ignoreCase?"i":"")+(f.multiline?"m":"")+(f.unicode?"u":"")+(h?"y":"g")),b=void 0===n?4294967295:n>>>0;if(0===b)return[];if(0===l.length)return null===a(g,l)?[l]:[];for(var m=0,_=0,k=[];_document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a.prototype[i[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c.prototype=e(t),r=new c,c.prototype=null,r[u]=t):r=a(),void 0===n?r:o(r,n)}},L9s1:function(t,n,r){"use strict";var e=r("XKFU"),o=r("0sh+");e(e.P+e.F*r("UUeW")("includes"),"String",{includes:function(t){return!!~o(this,t,"includes").indexOf(t,arguments.length>1?arguments[1]:void 0)}})},LK8F:function(t,n,r){var e=r("XKFU");e(e.S,"Array",{isArray:r("EWmC")})},LQAc:function(t,n){t.exports=!1},LVwc:function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},LZWt:function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},Ljet:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},Lmuc:function(t,n,r){r("xfY5"),r("A2zW"),r("VKir"),r("Ljet"),r("/KAi"),r("fN96"),r("7h0T"),r("sbF8"),r("h/M4"),r("knhD"),r("XfKG"),r("BP8U"),t.exports=r("g3g5").Number},LyE8:function(t,n,r){"use strict";var e=r("eeVq");t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},M6Qj:function(t,n,r){var e=r("hPIQ"),o=r("K0xU")("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||i[o]===t)}},MfQN:function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},MtdB:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},Mukb:function(t,n,r){var e=r("hswa"),o=r("RjD/");t.exports=r("nh4g")?function(t,n,r){return e.f(t,n,o(1,r))}:function(t,n,r){return t[n]=r,t}},N8g3:function(t,n,r){n.f=r("K0xU")},Nr18:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=function(t){for(var n=e(this),r=i(n.length),u=arguments.length,c=o(u>1?arguments[1]:void 0,r),a=u>2?arguments[2]:void 0,f=void 0===a?r:o(a,r);f>c;)n[c++]=t;return n}},Nz9U:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=[].join;e(e.P+e.F*(r("Ymqv")!=Object||!r("LyE8")(i)),"Array",{join:function(t){return i.call(o(this),void 0===t?",":t)}})},OEbY:function(t,n,r){r("nh4g")&&"g"!=/./g.flags&&r("hswa").f(RegExp.prototype,"flags",{configurable:!0,get:r("C/va")})},OG14:function(t,n,r){"use strict";var e=r("y3w9"),o=r("g6HL"),i=r("Xxuz");r("IU+Z")("search",1,function(t,n,r,u){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=u(r,t,this);if(n.done)return n.value;var c=e(t),a=String(this),f=c.lastIndex;o(f,0)||(c.lastIndex=0);var s=i(c,a);return o(c.lastIndex,f)||(c.lastIndex=f),null===s?-1:s.index}]})},OGtf:function(t,n,r){var e=r("XKFU"),o=r("eeVq"),i=r("vhPU"),u=/"/g,c=function(t,n,r,e){var o=String(i(t)),c="<"+n;return""!==r&&(c+=" "+r+'="'+String(e).replace(u,""")+'"'),c+">"+o+""};t.exports=function(t,n){var r={};r[t]=n(c),e(e.P+e.F*o(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},OP3Y:function(t,n,r){var e=r("aagx"),o=r("S/j/"),i=r("YTvA")("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=o(t),e(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},OnI7:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("LQAc"),u=r("N8g3"),c=r("hswa").f;t.exports=function(t){var n=o.Symbol||(o.Symbol=i?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},Oyvg:function(t,n,r){var e=r("dyZX"),o=r("Xbzi"),i=r("hswa").f,u=r("kJMx").f,c=r("quPj"),a=r("C/va"),f=e.RegExp,s=f,l=f.prototype,h=/a/g,p=/a/g,v=new f(h)!==h;if(r("nh4g")&&(!v||r("eeVq")(function(){return p[r("K0xU")("match")]=!1,f(h)!=h||f(p)==p||"/a/i"!=f(h,"i")}))){f=function(t,n){var r=this instanceof f,e=c(t),i=void 0===n;return!r&&e&&t.constructor===f&&i?t:o(v?new s(e&&!i?t.source:t,n):s((e=t instanceof f)?t.source:t,e&&i?a.call(t):n),r?this:l,f)};for(var y=function(t){t in f||i(f,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})},d=u(s),g=0;d.length>g;)y(d[g++]);l.constructor=f,f.prototype=l,r("KroJ")(e,"RegExp",f)}r("elZq")("RegExp")},PKUr:function(t,n,r){var e=r("dyZX").parseInt,o=r("qncB").trim,i=r("/e88"),u=/^[-+]?0[xX]/;t.exports=8!==e(i+"08")||22!==e(i+"0x16")?function(t,n){var r=o(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},QaDb:function(t,n,r){"use strict";var e=r("Kuth"),o=r("RjD/"),i=r("fyDq"),u={};r("Mukb")(u,r("K0xU")("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:o(1,r)}),i(t,n+" Iterator")}},RW0V:function(t,n,r){var e=r("S/j/"),o=r("DVgA");r("Xtr8")("keys",function(){return function(t){return o(e(t))}})},RYi7:function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},"RjD/":function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},"S/j/":function(t,n,r){var e=r("vhPU");t.exports=function(t){return Object(e(t))}},SMB2:function(t,n,r){"use strict";r("OGtf")("bold",function(t){return function(){return t(this,"b","","")}})},SPin:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduceRight,!0),"Array",{reduceRight:function(t){return o(this,t,arguments.length,arguments[1],!0)}})},SRfc:function(t,n,r){"use strict";var e=r("y3w9"),o=r("ne8i"),i=r("A5AN"),u=r("Xxuz");r("IU+Z")("match",1,function(t,n,r,c){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=c(r,t,this);if(n.done)return n.value;var a=e(t),f=String(this);if(!a.global)return u(a,f);var s=a.unicode;a.lastIndex=0;for(var l,h=[],p=0;null!==(l=u(a,f));){var v=String(l[0]);h[p]=v,""===v&&(a.lastIndex=i(f,o(a.lastIndex),s)),p++}return 0===p?null:h}]})},SlkY:function(t,n,r){var e=r("m0Pp"),o=r("H6hf"),i=r("M6Qj"),u=r("y3w9"),c=r("ne8i"),a=r("J+6e"),f={},s={};(n=t.exports=function(t,n,r,l,h){var p,v,y,d,g=h?function(){return t}:a(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(i(g)){for(p=c(t.length);p>m;m++)if((d=n?b(u(v=t[m])[0],v[1]):b(t[m]))===f||d===s)return d}else for(y=g.call(t);!(v=y.next()).done;)if((d=o(y,b,v.value,n))===f||d===s)return d}).BREAK=f,n.RETURN=s},T39b:function(t,n,r){"use strict";var e=r("wmvG"),o=r("s5qY");t.exports=r("4LiD")("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(o(this,"Set"),t=0===t?0:t,t)}},e)},Tze0:function(t,n,r){"use strict";r("qncB")("trim",function(t){return function(){return t(this,3)}})},U2t9:function(t,n,r){var e=r("XKFU"),o=Math.asinh;e(e.S+e.F*!(o&&1/o(0)>0),"Math",{asinh:function t(n){return isFinite(n=+n)&&0!=n?n<0?-t(-n):Math.log(n+Math.sqrt(n*n+1)):n}})},UUeW:function(t,n,r){var e=r("K0xU")("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(o){}}return!0}},Ugos:function(t,n,r){"use strict";var e,o,i=r("C/va"),u=RegExp.prototype.exec,c=String.prototype.replace,a=u,f=(o=/b*/g,u.call(e=/a/,"a"),u.call(o,"a"),0!==e.lastIndex||0!==o.lastIndex),s=void 0!==/()??/.exec("")[1];(f||s)&&(a=function(t){var n,r,e,o,a=this;return s&&(r=new RegExp("^"+a.source+"$(?!\\s)",i.call(a))),f&&(n=a.lastIndex),e=u.call(a,t),f&&e&&(a.lastIndex=a.global?e.index+e[0].length:n),s&&e&&e.length>1&&c.call(e[0],r,function(){for(o=1;ou;){if(n=+arguments[u++],o(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?i(n):i(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},WLL4:function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperties:r("FJW5")})},XKFU:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("Mukb"),u=r("KroJ"),c=r("m0Pp"),a=function(t,n,r){var f,s,l,h,p=t&a.F,v=t&a.G,y=t&a.P,d=t&a.B,g=v?e:t&a.S?e[n]||(e[n]={}):(e[n]||{}).prototype,b=v?o:o[n]||(o[n]={}),m=b.prototype||(b.prototype={});for(f in v&&(r=n),r)l=((s=!p&&g&&void 0!==g[f])?g:r)[f],h=d&&s?c(l,e):y&&"function"==typeof l?c(Function.call,l):l,g&&u(g,f,l,t&a.U),b[f]!=l&&i(b,f,h),y&&m[f]!=l&&(m[f]=l)};e.core=o,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},XMVh:function(t,n,r){var e=r("K0xU")("iterator"),o=!1;try{var i=[7][e]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(u){}t.exports=function(t,n){if(!n&&!o)return!1;var r=!1;try{var i=[7],c=i[e]();c.next=function(){return{done:r=!0}},i[e]=function(){return c},t(i)}catch(u){}return r}},Xbzi:function(t,n,r){var e=r("0/R4"),o=r("i5dc").set;t.exports=function(t,n,r){var i,u=n.constructor;return u!==r&&"function"==typeof u&&(i=u.prototype)!==r.prototype&&e(i)&&o&&o(t,i),t}},XfKG:function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.S+e.F*(Number.parseFloat!=o),"Number",{parseFloat:o})},XfO3:function(t,n,r){"use strict";var e=r("AvRE")(!0);r("Afnz")(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},Xtr8:function(t,n,r){var e=r("XKFU"),o=r("g3g5"),i=r("eeVq");t.exports=function(t,n){var r=(o.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*i(function(){r(1)}),"Object",u)}},Xxuz:function(t,n,r){"use strict";var e=r("I8a+"),o=RegExp.prototype.exec;t.exports=function(t,n){var r=t.exec;if("function"==typeof r){var i=r.call(t,n);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==e(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},YJVH:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(4);e(e.P+e.F*!r("LyE8")([].every,!0),"Array",{every:function(t){return o(this,t,arguments[1])}})},YTvA:function(t,n,r){var e=r("VTer")("keys"),o=r("ylqs");t.exports=function(t){return e[t]||(e[t]=o(t))}},Ymqv:function(t,n,r){var e=r("LZWt");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},Z6vF:function(t,n,r){var e=r("ylqs")("meta"),o=r("0/R4"),i=r("aagx"),u=r("hswa").f,c=0,a=Object.isExtensible||function(){return!0},f=!r("eeVq")(function(){return a(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=t.exports={KEY:e,NEED:!1,fastKey:function(t,n){if(!o(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,e)){if(!a(t))return"F";if(!n)return"E";s(t)}return t[e].i},getWeak:function(t,n){if(!i(t,e)){if(!a(t))return!0;if(!n)return!1;s(t)}return t[e].w},onFreeze:function(t){return f&&l.NEED&&a(t)&&!i(t,e)&&s(t),t}}},ZD67:function(t,n,r){"use strict";var e=r("3Lyj"),o=r("Z6vF").getWeak,i=r("y3w9"),u=r("0/R4"),c=r("9gX7"),a=r("SlkY"),f=r("CkkT"),s=r("aagx"),l=r("s5qY"),h=f(5),p=f(6),v=0,y=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},g=function(t,n){return h(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=g(this,t);if(n)return n[1]},has:function(t){return!!g(this,t)},set:function(t,n){var r=g(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=p(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,i){var f=t(function(t,e){c(t,f,n,"_i"),t._t=n,t._i=v++,t._l=void 0,null!=e&&a(e,r,t[i],t)});return e(f.prototype,{delete:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).delete(t):r&&s(r,this._i)&&delete r[this._i]},has:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).has(t):r&&s(r,this._i)}}),f},def:function(t,n,r){var e=o(i(n),!0);return!0===e?y(t).set(n,r):e[t._i]=r,t},ufstore:y}},Zshi:function(t,n,r){var e=r("0/R4");r("Xtr8")("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},Zz4T:function(t,n,r){"use strict";r("OGtf")("sub",function(t){return function(){return t(this,"sub","","")}})},a1Th:function(t,n,r){"use strict";r("OEbY");var e=r("y3w9"),o=r("C/va"),i=r("nh4g"),u=/./.toString,c=function(t){r("KroJ")(RegExp.prototype,"toString",t,!0)};r("eeVq")(function(){return"/a/b"!=u.call({source:"a",flags:"b"})})?c(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!i&&t instanceof RegExp?o.call(t):void 0)}):"toString"!=u.name&&c(function(){return u.call(this)})},aCFj:function(t,n,r){var e=r("Ymqv"),o=r("vhPU");t.exports=function(t){return e(o(t))}},aagx:function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},apmT:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t))return t;var r,o;if(n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;if("function"==typeof(r=t.valueOf)&&!e(o=r.call(t)))return o;if(!n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},bBoP:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S+e.F*r("eeVq")(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(o(t)-o(-t))/2:(i(t-1)-i(-t-1))*(Math.E/2)}})},bDcW:function(t,n,r){"use strict";r("OGtf")("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},bHtr:function(t,n,r){var e=r("XKFU");e(e.P,"Array",{fill:r("Nr18")}),r("nGyu")("fill")},bWfx:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(1);e(e.P+e.F*!r("LyE8")([].map,!0),"Array",{map:function(t){return o(this,t,arguments[1])}})},czNK:function(t,n,r){"use strict";var e=r("DVgA"),o=r("JiEa"),i=r("UqcF"),u=r("S/j/"),c=r("Ymqv"),a=Object.assign;t.exports=!a||r("eeVq")(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=a({},t)[r]||Object.keys(a({},n)).join("")!=e})?function(t,n){for(var r=u(t),a=arguments.length,f=1,s=o.f,l=i.f;a>f;)for(var h,p=c(arguments[f++]),v=s?e(p).concat(s(p)):e(p),y=v.length,d=0;y>d;)l.call(p,h=v[d++])&&(r[h]=p[h]);return r}:a},"d/Gc":function(t,n,r){var e=r("RYi7"),o=Math.max,i=Math.min;t.exports=function(t,n){return(t=e(t))<0?o(t+n,0):i(t,n)}},"dE+T":function(t,n,r){var e=r("XKFU");e(e.P,"Array",{copyWithin:r("upKx")}),r("nGyu")("copyWithin")},dQfE:function(t,n,r){r("XfO3"),r("LK8F"),r("HEwt"),r("6AQ9"),r("Nz9U"),r("I78e"),r("Vd3H"),r("8+KV"),r("bWfx"),r("0l/t"),r("dZ+Y"),r("YJVH"),r("DNiP"),r("SPin"),r("V+eJ"),r("mGWK"),r("dE+T"),r("bHtr"),r("dRSK"),r("INYr"),r("0E+W"),r("yt8O"),t.exports=r("g3g5").Array},dRSK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(5),i=!0;"find"in[]&&Array(1).find(function(){i=!1}),e(e.P+e.F*i,"Array",{find:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}}),r("nGyu")("find")},"dZ+Y":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(3);e(e.P+e.F*!r("LyE8")([].some,!0),"Array",{some:function(t){return o(this,t,arguments[1])}})},dyZX:function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},e7yV:function(t,n,r){var e=r("aCFj"),o=r("kJMx").f,i={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return u&&"[object Window]"==i.call(t)?function(t){try{return o(t)}catch(n){return u.slice()}}(t):o(e(t))}},eHKK:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log10:function(t){return Math.log(t)*Math.LOG10E}})},eI33:function(t,n,r){var e=r("XKFU"),o=r("aCFj"),i=r("ne8i");e(e.S,"String",{raw:function(t){for(var n=o(t.raw),r=i(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c=0:l>h;h+=p)h in s&&(c=n(c,s[h],h,f));return c}},"f3/d":function(t,n,r){var e=r("hswa").f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||r("nh4g")&&e(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},fN96:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isInteger:r("nBIS")})},fyDq:function(t,n,r){var e=r("hswa").f,o=r("aagx"),i=r("K0xU")("toStringTag");t.exports=function(t,n,r){t&&!o(t=r?t:t.prototype,i)&&e(t,i,{configurable:!0,value:n})}},fyVe:function(t,n,r){var e=r("XKFU"),o=r("1sa7"),i=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:o(t-1+i(t-1)*i(t+1))}})},g3g5:function(t,n){var r=t.exports={version:"2.6.1"};"number"==typeof __e&&(__e=r)},g4EE:function(t,n,r){"use strict";var e=r("y3w9"),o=r("apmT");t.exports=function(t){if("string"!==t&&"number"!==t&&"default"!==t)throw TypeError("Incorrect hint");return o(e(this),"number"!=t)}},g6HL:function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},"h/M4":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},h7Nl:function(t,n,r){var e=Date.prototype,o=e.toString,i=e.getTime;new Date(NaN)+""!="Invalid Date"&&r("KroJ")(e,"toString",function(){var t=i.call(this);return t==t?o.call(this):"Invalid Date"})},hEkN:function(t,n,r){"use strict";r("OGtf")("anchor",function(t){return function(n){return t(this,"a","name",n)}})},hHhE:function(t,n,r){var e=r("XKFU");e(e.S,"Object",{create:r("Kuth")})},hLT2:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},"hN/g":function(t,n,r){"use strict";r.r(n),r("dQfE"),r("nx1v"),r("4A4+"),r("qKs0"),r("CuTL"),r("Lmuc"),r("99sg"),r("ifmr"),r("oka+"),r("rfyP"),r("VXxg"),r("V5/Y"),r("vqGA"),r("hYbK"),r("0TWp")},hPIQ:function(t,n){t.exports={}},hYbK:function(t,n,r){r("Btvt"),r("yt8O"),r("EK0E"),t.exports=r("g3g5").WeakMap},hswa:function(t,n,r){var e=r("y3w9"),o=r("xpql"),i=r("apmT"),u=Object.defineProperty;n.f=r("nh4g")?Object.defineProperty:function(t,n,r){if(e(t),n=i(n,!0),e(r),o)try{return u(t,n,r)}catch(c){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},i5dc:function(t,n,r){var e=r("0/R4"),o=r("y3w9"),i=function(t,n){if(o(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{(e=r("m0Pp")(Function.call,r("EemH").f(Object.prototype,"__proto__").set,2))(t,[]),n=!(t instanceof Array)}catch(o){n=!0}return function(t,r){return i(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:i}},ifmr:function(t,n,r){r("tyy+"),t.exports=r("g3g5").parseFloat},ioFf:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("nh4g"),u=r("XKFU"),c=r("KroJ"),a=r("Z6vF").KEY,f=r("eeVq"),s=r("VTer"),l=r("fyDq"),h=r("ylqs"),p=r("K0xU"),v=r("N8g3"),y=r("OnI7"),d=r("1MBn"),g=r("EWmC"),b=r("y3w9"),m=r("0/R4"),_=r("aCFj"),k=r("apmT"),w=r("RjD/"),x=r("Kuth"),S=r("e7yV"),F=r("EemH"),T=r("hswa"),O=r("DVgA"),j=F.f,M=T.f,K=S.f,U=e.Symbol,X=e.JSON,Z=X&&X.stringify,z=p("_hidden"),E=p("toPrimitive"),D={}.propertyIsEnumerable,A=s("symbol-registry"),L=s("symbols"),I=s("op-symbols"),P=Object.prototype,C="function"==typeof U,q=e.QObject,V=!q||!q.prototype||!q.prototype.findChild,R=i&&f(function(){return 7!=x(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=j(P,n);e&&delete P[n],M(t,n,r),e&&t!==P&&M(P,n,e)}:M,G=function(t){var n=L[t]=x(U.prototype);return n._k=t,n},J=C&&"symbol"==typeof U.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof U},Y=function(t,n,r){return t===P&&Y(I,n,r),b(t),n=k(n,!0),b(r),o(L,n)?(r.enumerable?(o(t,z)&&t[z][n]&&(t[z][n]=!1),r=x(r,{enumerable:w(0,!1)})):(o(t,z)||M(t,z,w(1,{})),t[z][n]=!0),R(t,n,r)):M(t,n,r)},H=function(t,n){b(t);for(var r,e=d(n=_(n)),o=0,i=e.length;i>o;)Y(t,r=e[o++],n[r]);return t},N=function(t){var n=D.call(this,t=k(t,!0));return!(this===P&&o(L,t)&&!o(I,t))&&(!(n||!o(this,t)||!o(L,t)||o(this,z)&&this[z][t])||n)},W=function(t,n){if(t=_(t),n=k(n,!0),t!==P||!o(L,n)||o(I,n)){var r=j(t,n);return!r||!o(L,n)||o(t,z)&&t[z][n]||(r.enumerable=!0),r}},B=function(t){for(var n,r=K(_(t)),e=[],i=0;r.length>i;)o(L,n=r[i++])||n==z||n==a||e.push(n);return e},Q=function(t){for(var n,r=t===P,e=K(r?I:_(t)),i=[],u=0;e.length>u;)!o(L,n=e[u++])||r&&!o(P,n)||i.push(L[n]);return i};C||(c((U=function(){if(this instanceof U)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===P&&n.call(I,r),o(this,z)&&o(this[z],t)&&(this[z][t]=!1),R(this,t,w(1,r))};return i&&V&&R(P,t,{configurable:!0,set:n}),G(t)}).prototype,"toString",function(){return this._k}),F.f=W,T.f=Y,r("kJMx").f=S.f=B,r("UqcF").f=N,r("JiEa").f=Q,i&&!r("LQAc")&&c(P,"propertyIsEnumerable",N,!0),v.f=function(t){return G(p(t))}),u(u.G+u.W+u.F*!C,{Symbol:U});for(var $="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),tt=0;$.length>tt;)p($[tt++]);for(var nt=O(p.store),rt=0;nt.length>rt;)y(nt[rt++]);u(u.S+u.F*!C,"Symbol",{for:function(t){return o(A,t+="")?A[t]:A[t]=U(t)},keyFor:function(t){if(!J(t))throw TypeError(t+" is not a symbol!");for(var n in A)if(A[n]===t)return n},useSetter:function(){V=!0},useSimple:function(){V=!1}}),u(u.S+u.F*!C,"Object",{create:function(t,n){return void 0===n?x(t):H(x(t),n)},defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:W,getOwnPropertyNames:B,getOwnPropertySymbols:Q}),X&&u(u.S+u.F*(!C||f(function(){var t=U();return"[null]"!=Z([t])||"{}"!=Z({a:t})||"{}"!=Z(Object(t))})),"JSON",{stringify:function(t){for(var n,r,e=[t],o=1;arguments.length>o;)e.push(arguments[o++]);if(r=n=e[1],(m(n)||void 0!==t)&&!J(t))return g(n)||(n=function(t,n){if("function"==typeof r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,Z.apply(X,e)}}),U.prototype[E]||r("Mukb")(U.prototype,E,U.prototype.valueOf),l(U,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},jqX0:function(t,n,r){var e=r("XKFU"),o=r("jtBr");e(e.P+e.F*(Date.prototype.toISOString!==o),"Date",{toISOString:o})},jtBr:function(t,n,r){"use strict";var e=r("eeVq"),o=Date.prototype.getTime,i=Date.prototype.toISOString,u=function(t){return t>9?t:"0"+t};t.exports=e(function(){return"0385-07-25T07:06:39.999Z"!=i.call(new Date(-5e13-1))})||!e(function(){i.call(new Date(NaN))})?function(){if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}:i},kJMx:function(t,n,r){var e=r("zhAb"),o=r("4R4u").concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,o)}},kcoS:function(t,n,r){var e=r("lvtm"),o=Math.pow,i=o(2,-52),u=o(2,-23),c=o(2,127)*(2-u),a=o(2,-126);t.exports=Math.fround||function(t){var n,r,o=Math.abs(t),f=e(t);return oc||r!=r?f*(1/0):f*r}},knhD:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},l0Rn:function(t,n,r){"use strict";var e=r("RYi7"),o=r("vhPU");t.exports=function(t){var n=String(o(this)),r="",i=e(t);if(i<0||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(n+=n))1&i&&(r+=n);return r}},lvtm:function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},m0Pp:function(t,n,r){var e=r("2OiF");t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,o){return t.call(n,r,e,o)}}return function(){return t.apply(n,arguments)}}},mGWK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=r("RYi7"),u=r("ne8i"),c=[].lastIndexOf,a=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(a||!r("LyE8")(c)),"Array",{lastIndexOf:function(t){if(a)return c.apply(this,arguments)||0;var n=o(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,i(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},mYba:function(t,n,r){var e=r("aCFj"),o=r("EemH").f;r("Xtr8")("getOwnPropertyDescriptor",function(){return function(t,n){return o(e(t),n)}})},mura:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("preventExtensions",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},nBIS:function(t,n,r){var e=r("0/R4"),o=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&o(t)===t}},nGyu:function(t,n,r){var e=r("K0xU")("unscopables"),o=Array.prototype;null==o[e]&&r("Mukb")(o,e,{}),t.exports=function(t){o[e][t]=!0}},nIY7:function(t,n,r){"use strict";r("OGtf")("big",function(t){return function(){return t(this,"big","","")}})},ne8i:function(t,n,r){var e=r("RYi7"),o=Math.min;t.exports=function(t){return t>0?o(e(t),9007199254740991):0}},nh4g:function(t,n,r){t.exports=!r("eeVq")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},nsiH:function(t,n,r){"use strict";r("OGtf")("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},nx1v:function(t,n,r){r("eM6i"),r("AphP"),r("jqX0"),r("h7Nl"),r("yM4b"),t.exports=Date},nzyx:function(t,n,r){var e=r("XKFU"),o=r("LVwc");e(e.S+e.F*(o!=Math.expm1),"Math",{expm1:o})},oDIu:function(t,n,r){"use strict";var e=r("XKFU"),o=r("AvRE")(!1);e(e.P,"String",{codePointAt:function(t){return o(this,t)}})},"oka+":function(t,n,r){r("GNAe"),t.exports=r("g3g5").parseInt},pIFo:function(t,n,r){"use strict";var e=r("y3w9"),o=r("S/j/"),i=r("ne8i"),u=r("RYi7"),c=r("A5AN"),a=r("Xxuz"),f=Math.max,s=Math.min,l=Math.floor,h=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g;r("IU+Z")("replace",2,function(t,n,r,v){return[function(e,o){var i=t(this),u=null==e?void 0:e[n];return void 0!==u?u.call(e,i,o):r.call(String(i),e,o)},function(t,n){var o=v(r,t,this,n);if(o.done)return o.value;var l=e(t),h=String(this),p="function"==typeof n;p||(n=String(n));var d=l.global;if(d){var g=l.unicode;l.lastIndex=0}for(var b=[];;){var m=a(l,h);if(null===m)break;if(b.push(m),!d)break;""===String(m[0])&&(l.lastIndex=c(h,i(l.lastIndex),g))}for(var _,k="",w=0,x=0;x=w&&(k+=h.slice(w,F)+K,w=F+S.length)}return k+h.slice(w)}];function y(t,n,e,i,u,c){var a=e+t.length,f=i.length,s=p;return void 0!==u&&(u=o(u),s=h),r.call(c,s,function(r,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,e);case"'":return n.slice(a);case"<":c=u[o.slice(1,-1)];break;default:var s=+o;if(0===s)return o;if(s>f){var h=l(s/10);return 0===h?o:h<=f?void 0===i[h-1]?o.charAt(1):i[h-1]+o.charAt(1):o}c=i[s-1]}return void 0===c?"":c})}})},"pp/T":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},qKs0:function(t,n,r){r("Btvt"),r("XfO3"),r("rGqo"),r("9AAn"),t.exports=r("g3g5").Map},qncB:function(t,n,r){var e=r("XKFU"),o=r("vhPU"),i=r("eeVq"),u=r("/e88"),c="["+u+"]",a=RegExp("^"+c+c+"*"),f=RegExp(c+c+"*$"),s=function(t,n,r){var o={},c=i(function(){return!!u[t]()||"\u200b\x85"!="\u200b\x85"[t]()}),a=o[t]=c?n(l):u[t];r&&(o[r]=a),e(e.P+e.F*c,"String",o)},l=s.trim=function(t,n){return t=String(o(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(f,"")),t};t.exports=s},quPj:function(t,n,r){var e=r("0/R4"),o=r("LZWt"),i=r("K0xU")("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[i])?!!n:"RegExp"==o(t))}},rGqo:function(t,n,r){for(var e=r("yt8O"),o=r("DVgA"),i=r("KroJ"),u=r("dyZX"),c=r("Mukb"),a=r("hPIQ"),f=r("K0xU"),s=f("iterator"),l=f("toStringTag"),h=a.Array,p={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},v=o(p),y=0;y1?arguments[1]:void 0,e=o(n.length),c=void 0===r?e:Math.min(o(r),e),a=String(t);return u?u.call(n,a,c):n.slice(c-a.length,c)===a}})},s5qY:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t)||t._t!==n)throw TypeError("Incompatible receiver, "+n+" required!");return t}},sMXx:function(t,n,r){"use strict";var e=r("Ugos");r("XKFU")({target:"RegExp",proto:!0,forced:e!==/./.exec},{exec:e})},sbF8:function(t,n,r){var e=r("XKFU"),o=r("nBIS"),i=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return o(t)&&i(t)<=9007199254740991}})},tUrg:function(t,n,r){"use strict";r("OGtf")("link",function(t){return function(n){return t(this,"a","href",n)}})},"tyy+":function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.G+e.F*(parseFloat!=o),{parseFloat:o})},upKx:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=[].copyWithin||function(t,n){var r=e(this),u=i(r.length),c=o(t,u),a=o(n,u),f=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===f?u:o(f,u))-a,u-c),l=1;for(a0;)a in r?r[c]=r[a]:delete r[c],c+=l,a+=l;return r}},vhPU:function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},vqGA:function(t,n,r){r("ioFf"),r("Btvt"),t.exports=r("g3g5").Symbol},vvmO:function(t,n,r){var e=r("LZWt");t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},w2a5:function(t,n,r){var e=r("aCFj"),o=r("ne8i"),i=r("d/Gc");t.exports=function(t){return function(n,r,u){var c,a=e(n),f=o(a.length),s=i(u,f);if(t&&r!=r){for(;f>s;)if((c=a[s++])!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===r)return t||s||0;return!t&&-1}}},wmvG:function(t,n,r){"use strict";var e=r("hswa").f,o=r("Kuth"),i=r("3Lyj"),u=r("m0Pp"),c=r("9gX7"),a=r("SlkY"),f=r("Afnz"),s=r("1TsA"),l=r("elZq"),h=r("nh4g"),p=r("Z6vF").fastKey,v=r("s5qY"),y=h?"_s":"size",d=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,f){var s=t(function(t,e){c(t,s,n,"_i"),t._t=n,t._i=o(null),t._f=void 0,t._l=void 0,t[y]=0,null!=e&&a(e,r,t[f],t)});return i(s.prototype,{clear:function(){for(var t=v(this,n),r=t._i,e=t._f;e;e=e.n)e.r=!0,e.p&&(e.p=e.p.n=void 0),delete r[e.i];t._f=t._l=void 0,t[y]=0},delete:function(t){var r=v(this,n),e=d(r,t);if(e){var o=e.n,i=e.p;delete r._i[e.i],e.r=!0,i&&(i.n=o),o&&(o.p=i),r._f==e&&(r._f=o),r._l==e&&(r._l=i),r[y]--}return!!e},forEach:function(t){v(this,n);for(var r,e=u(t,arguments.length>1?arguments[1]:void 0,3);r=r?r.n:this._f;)for(e(r.v,r.k,this);r&&r.r;)r=r.p},has:function(t){return!!d(v(this,n),t)}}),h&&e(s.prototype,"size",{get:function(){return v(this,n)[y]}}),s},def:function(t,n,r){var e,o,i=d(t,n);return i?i.v=r:(t._l=i={i:o=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=i),e&&(e.n=i),t[y]++,"F"!==o&&(t._i[o]=i)),t},getEntry:d,setStrong:function(t,n,r){f(t,n,function(t,r){this._t=v(t,n),this._k=r,this._l=void 0},function(){for(var t=this._k,n=this._l;n&&n.r;)n=n.p;return this._t&&(this._l=n=n?n.n:this._t._f)?s(0,"keys"==t?n.k:"values"==t?n.v:[n.k,n.v]):(this._t=void 0,s(1))},r?"entries":"values",!r,!0),l(n)}}},x8Yj:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S,"Math",{tanh:function(t){var n=o(t=+t),r=o(-t);return n==1/0?1:r==1/0?-1:(n-r)/(i(t)+i(-t))}})},x8ZO:function(t,n,r){var e=r("XKFU"),o=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,i=0,u=0,c=arguments.length,a=0;u0?(e=r/a)*e:r;return a===1/0?1/0:a*Math.sqrt(i)}})},xfY5:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("LZWt"),u=r("Xbzi"),c=r("apmT"),a=r("eeVq"),f=r("kJMx").f,s=r("EemH").f,l=r("hswa").f,h=r("qncB").trim,p=e.Number,v=p,y=p.prototype,d="Number"==i(r("Kuth")(y)),g="trim"in String.prototype,b=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){var r,e,o,i=(n=g?n.trim():h(n,3)).charCodeAt(0);if(43===i||45===i){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===i){switch(n.charCodeAt(1)){case 66:case 98:e=2,o=49;break;case 79:case 111:e=8,o=55;break;default:return+n}for(var u,a=n.slice(2),f=0,s=a.length;fo)return NaN;return parseInt(a,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(d?a(function(){y.valueOf.call(r)}):"Number"!=i(r))?u(new v(b(n)),r,p):b(n)};for(var m,_=r("nh4g")?f(v):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),k=0;_.length>k;k++)o(v,m=_[k])&&!o(p,m)&&l(p,m,s(v,m));p.prototype=y,y.constructor=p,r("KroJ")(e,"Number",p)}},xpql:function(t,n,r){t.exports=!r("nh4g")&&!r("eeVq")(function(){return 7!=Object.defineProperty(r("Iw71")("div"),"a",{get:function(){return 7}}).a})},y3w9:function(t,n,r){var e=r("0/R4");t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},yM4b:function(t,n,r){var e=r("K0xU")("toPrimitive"),o=Date.prototype;e in o||r("Mukb")(o,e,r("g4EE"))},ylqs:function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},yt8O:function(t,n,r){"use strict";var e=r("nGyu"),o=r("1TsA"),i=r("hPIQ"),u=r("aCFj");t.exports=r("Afnz")(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,o(1)):o(0,"keys"==n?r:"values"==n?t[r]:[r,t[r]])},"values"),i.Arguments=i.Array,e("keys"),e("values"),e("entries")},z2o2:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("seal",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},zRwo:function(t,n,r){var e=r("6FMO");t.exports=function(t,n){return new(e(t))(n)}},zhAb:function(t,n,r){var e=r("aagx"),o=r("aCFj"),i=r("w2a5")(!1),u=r("YTvA")("IE_PROTO");t.exports=function(t,n){var r,c=o(t),a=0,f=[];for(r in c)r!=u&&e(c,r)&&f.push(r);for(;n.length>a;)e(c,r=n[a++])&&(~i(f,r)||f.push(r));return f}}},[[1,0]]]); - -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{0:function(n,t,e){n.exports=e("zUnb")},crnd:function(n,t){function e(n){return Promise.resolve().then(function(){var t=new Error("Cannot find module '"+n+"'");throw t.code="MODULE_NOT_FOUND",t})}e.keys=function(){return[]},e.resolve=e,n.exports=e,e.id="crnd"},zUnb:function(n,t,e){"use strict";e.r(t);var r=function(n,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,t){n.__proto__=t}||function(n,t){for(var e in t)t.hasOwnProperty(e)&&(n[e]=t[e])})(n,t)};function o(n,t){function e(){this.constructor=n}r(n,t),n.prototype=null===t?Object.create(t):(e.prototype=t.prototype,new e)}var i=function(){return(i=Object.assign||function(n){for(var t,e=1,r=arguments.length;e=0;u--)(o=n[u])&&(l=(i<3?o(l):i>3?o(t,e,l):o(t,e))||l);return i>3&&l&&Object.defineProperty(t,e,l),l}function u(n,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(n,t)}function s(n){var t="function"==typeof Symbol&&n[Symbol.iterator],e=0;return t?t.call(n):{next:function(){return n&&e>=n.length&&(n=void 0),{value:n&&n[e++],done:!n}}}}function a(n,t){var e="function"==typeof Symbol&&n[Symbol.iterator];if(!e)return n;var r,o,i=e.call(n),l=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)l.push(r.value)}catch(u){o={error:u}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(o)throw o.error}}return l}function c(){for(var n=[],t=0;t0?this._next(t.shift()):0===this.active&&this.hasCompleted&&this.destination.complete()},t}(W);function rn(n){return n}function on(){return function(n){return n.lift(new ln(n))}}var ln=function(){function n(n){this.connectable=n}return n.prototype.call=function(n,t){var e=this.connectable;e._refCount++;var r=new un(n,e),o=t.subscribe(r);return r.closed||(r.connection=e.connect()),o},n}(),un=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._refCount;if(t<=0)this.connection=null;else if(n._refCount=t-1,t>1)this.connection=null;else{var e=this.connection,r=n._connection;this.connection=null,!r||e&&r!==e||r.unsubscribe()}}else this.connection=null},t}(S),sn=function(n){function t(t,e){var r=n.call(this)||this;return r.source=t,r.subjectFactory=e,r._refCount=0,r._isComplete=!1,r}return o(t,n),t.prototype._subscribe=function(n){return this.getSubject().subscribe(n)},t.prototype.getSubject=function(){var n=this._subject;return n&&!n.isStopped||(this._subject=this.subjectFactory()),this._subject},t.prototype.connect=function(){var n=this._connection;return n||(this._isComplete=!1,(n=this._connection=new _).add(this.source.subscribe(new cn(this.getSubject(),this))),n.closed?(this._connection=null,n=_.EMPTY):this._connection=n),n},t.prototype.refCount=function(){return on()(this)},t}(I).prototype,an={operator:{value:null},_refCount:{value:0,writable:!0},_subject:{value:null,writable:!0},_connection:{value:null,writable:!0},_subscribe:{value:sn._subscribe},_isComplete:{value:sn._isComplete,writable:!0},getSubject:{value:sn.getSubject},connect:{value:sn.connect},refCount:{value:sn.refCount}},cn=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._error=function(t){this._unsubscribe(),n.prototype._error.call(this,t)},t.prototype._complete=function(){this.connectable._isComplete=!0,this._unsubscribe(),n.prototype._complete.call(this)},t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._connection;n._refCount=0,n._subject=null,n._connection=null,t&&t.unsubscribe()}},t}(V);function fn(){return new H}function hn(n){for(var t in n)if(n[t]===hn)return t;throw Error("Could not find renamed property on target object.")}var pn=hn({ngComponentDef:hn}),dn=hn({ngInjectableDef:hn}),gn=hn({ngInjectorDef:hn}),vn=hn({ngModuleDef:hn}),yn=hn({__NG_ELEMENT_ID__:hn});function mn(n){return{providedIn:n.providedIn||null,factory:n.factory,value:void 0}}function bn(n){return n.hasOwnProperty(dn)?n[dn]:null}function _n(n){return n.hasOwnProperty(gn)?n[gn]:null}var wn=function(){function n(n,t){this._desc=n,this.ngMetadataName="InjectionToken",this.ngInjectableDef=void 0!==t?mn({providedIn:t.providedIn||"root",factory:t.factory}):void 0}return n.prototype.toString=function(){return"InjectionToken "+this._desc},n}(),Cn="__parameters__";function En(n,t,e){var r=function(n){return function(){for(var t=[],e=0;e=Yn?e:e[ot]}function Nt(n){return n[St]}function Dt(n){var t=Nt(n);return t?Array.isArray(t)?t:t.lViewData:null}function Mt(n){return 32767&n}function Vt(n,t){for(var e=n>>16,r=t;e>0;)r=r[pt],e--;return r}var Ht,Rt,jt,Lt,zt,Ft,Bt,Ut,Gt=("undefined"!=typeof requestAnimationFrame&&requestAnimationFrame||setTimeout).bind(Sn);function Zt(){return Ht}function qt(){return Rt}function $t(){return jt}function Qt(n){jt=n}function Wt(n,t){jt=n,Ut=t}function Kt(){return Lt}function Jt(n){Lt=n}function Yt(){return zt}function Xt(){return Bt}function ne(){return Ut}var te=!1;function ee(){return te}function re(n){te=n}var oe=!0;function ie(n){oe=n}function le(n,t){var e=Ut;return zt=n&&n[Xn],Bt=n&&1==(1&n[nt]),oe=n&&zt.firstTemplatePass,Ht=n&&n[ct],jt=t,Lt=!0,Ut=n,e&&(e[rt]=Ft),Ft=n&&n[rt],e}function ue(n,t){t||(te||yt(Ut,zt.viewHooks,zt.viewCheckHooks,Bt),Ut[nt]&=-6),Ut[nt]|=16,Ut[lt]=zt.bindingStartIndex,le(n,null)}var se=!1;function ae(n){var t=se;return se=n,t}var ce=255,fe=0;function he(n,t){var e=de(n,t);if(-1!==e)return e;var r=t[Xn];r.firstTemplatePass&&(n.injectorIndex=t.length,pe(r.data,n),pe(t,null),pe(r.blueprint,null));var o=ge(n,t),i=Mt(o),l=Vt(o,t),u=n.injectorIndex;if(o!==Wn)for(var s=l[Xn].data,a=0;a<8;a++)t[u+a]=l[i+a]|s[i+a];return t[u+$n]=o,u}function pe(n,t){n.push(0,0,0,0,0,0,0,0,t)}function de(n,t){return-1===n.injectorIndex||n.parent&&n.parent.injectorIndex===n.injectorIndex||null==t[n.injectorIndex+$n]?-1:n.injectorIndex}function ge(n,t){if(n.parent&&-1!==n.parent.injectorIndex)return n.parent.injectorIndex;for(var e=t[it],r=1;e&&-1===e.injectorIndex;)e=(t=t[pt])[it],r++;return e?e.injectorIndex|r<<16|(e&&3===e.type?32768:0):-1}var ve={};function ye(n,t,e,r){var o=t[Xn],i=o.data[n+qn],l=i.flags,u=i.providerIndexes,s=o.data,a=!1;(null==r&&function(n){return 4096==(4096&n.flags)}(i)&&se||null!=r&&r!=o&&(null==o.node||3===o.node.type))&&(a=!0);for(var c=65535&u,f=l>>16,h=4095&l,p=a?c:c+(u>>16);p=f&&d.type===e)return me(s,t,p,i)}return ve}function me(n,t,e,r){var o,i=t[e];if(null!=(o=i)&&"object"==typeof o&&Object.getPrototypeOf(o)==Jn){var l=i;if(l.resolving)throw new Error("Circular dep for "+At(n[e]));var u=ae(l.canSeeViewProviders);l.resolving=!0;var s=void 0;l.injectImpl&&(s=Bn(l.injectImpl));var a=$t(),c=ne();Wt(r,t);try{i=t[e]=l.factory(null,n,t,r)}finally{l.injectImpl&&Bn(s),ae(u),l.resolving=!1,Wt(a,c)}}return i}function be(n,t,e){var r=64&n,o=32&n;return!!((128&n?r?o?e[t+7]:e[t+6]:o?e[t+5]:e[t+4]:r?o?e[t+3]:e[t+2]:o?e[t+1]:e[t])&1< ");else if("object"==typeof t){var o=[];for(var i in t)if(t.hasOwnProperty(i)){var l=t[i];o.push(i+":"+("string"==typeof l?JSON.stringify(l):Dn(l)))}r="{"+o.join(", ")+"}"}return"StaticInjectorError"+(e?"("+e+")":"")+"["+r+"]: "+n.replace(ze,"\n ")}function Ze(n,t){return new Error(Ge(n,t))}var qe=function(){return function(){}}(),$e=function(){return function(){}}(),Qe="ngProjectAs";function We(n){return!!n.listen}var Ke={createRenderer:function(n,t){return document}},Je=[];function Ye(n){for(var t=n[it];t&&2===t.type;)t=(n=n[tt])[it];return n}function Xe(n,t,e,r,o){0===n?We(t)?t.insertBefore(e,r,o):e.insertBefore(r,o,!0):1===n?We(t)?t.removeChild(e,r):e.removeChild(r):2===n&&t.destroyNode(r)}function nr(n){var t=n[Xn].childIndex;return-1===t?null:n[t]}function tr(n,t){var e;return n.length>=Yn&&(e=n[it])&&2===e.type?function(t,e){if(-1===t.index){var r=n[ht];return r>-1?n[tt][r]:null}return n[tt][t.parent.index]}(e):n[tt]===t?null:n[tt]}function er(n){if(n.length>=Yn){var t=n;!function(n){var t=n[Xn].cleanup;if(null!=t){for(var e=0;e=Yn?t[Xn].childIndex>-1&&(e=nr(t)):t[Ot].length&&(e=t[Ot][0]),null==e){for(;t&&!t[et]&&t!==n;)er(t),t=tr(t,n);er(t||n),e=t&&t[et]}t=e}}(n),n[nt]|=32},n.prototype.onDestroy=function(n){var t,e;e=n,function(n){return n[ut]||(n[ut]=[])}(t=this._view).push(e),t[Xn].firstTemplatePass&&function(n){return n[Xn].cleanup||(n[Xn].cleanup=[])}(t).push(t[ut].length-1,null)},n.prototype.markForCheck=function(){!function(n){for(var t=n;t&&!(64&t[nt]);)t[nt]|=4,t=t[tt];var e,r,o;t[nt]|=4,o=0===(e=t[st]).flags,e.flags|=1,o&&e.clean==or&&(e.clean=new Promise(function(n){return r=n}),e.scheduler(function(){if(1&e.flags&&(e.flags&=-2,mr(e)),2&e.flags){e.flags&=-3;var n=e.playerHandler;n&&n.flushPlayers()}e.clean=or,r(null)}))}(this._view)},n.prototype.detach=function(){this._view[nt]&=-9},n.prototype.reattach=function(){this._view[nt]|=8},n.prototype.detectChanges=function(){var n=qt();n.begin&&n.begin(),br(this.context),n.end&&n.end()},n.prototype.checkNoChanges=function(){!function(n){re(!0);try{br(n)}finally{re(!1)}}(this.context)},n.prototype.attachToViewContainerRef=function(n){this._viewContainerRef=n},n.prototype.detachFromAppRef=function(){this._appRef=null},n.prototype.attachToAppRef=function(n){this._appRef=n},n.prototype._lookUpContext=function(){return this._context=this._view[tt][this._componentIndex]},n}());function Or(n,t,e,r,o){var i=e[Xn],l=function(n,t,e){var r=$t();n.firstTemplatePass&&(e.providersResolver&&e.providersResolver(e),function(n,t,e){var o=-(r.index-Yn),i=n.data.length-(65535&r.providerIndexes);(n.expandoInstructions||(n.expandoInstructions=[])).push(o,i,1)}(n),function(n,t,e,r){n.data.push(e);var o=new Kn(r,function(n){return null!==n.template}(e),null);n.blueprint.push(o),t.push(o),function(n,t){n.expandoInstructions.push(t.hostBindings||Ee),t.hostVars&&n.expandoInstructions.push(t.hostVars)}(n,e)}(n,t,e,e.factory));var o=me(n.data,t,t.length-1,r);return function(n,t,e,r){var o=It(t,n);Ce(e,n),o&&Ce(o,n),null!=r.attributes&&3==t.type&&function(n,t){for(var e=Zt(),r=We(e),o=0;o>16,r=e+(4095&n),o=e;o',!this.inertBodyElement.querySelector||this.inertBodyElement.querySelector("svg")?(this.inertBodyElement.innerHTML='

',this.getInertBodyElement=this.inertBodyElement.querySelector&&this.inertBodyElement.querySelector("svg img")&&function(){try{return!!window.DOMParser}catch(n){return!1}}()?this.getInertBodyElement_DOMParser:this.getInertBodyElement_InertDocument):this.getInertBodyElement=this.getInertBodyElement_XHR}return n.prototype.getInertBodyElement_XHR=function(n){n=""+n+"";try{n=encodeURI(n)}catch(r){return null}var t=new XMLHttpRequest;t.responseType="document",t.open("GET","data:text/html;charset=utf-8,"+n,!1),t.send(void 0);var e=t.response.body;return e.removeChild(e.firstChild),e},n.prototype.getInertBodyElement_DOMParser=function(n){n=""+n+"";try{var t=(new window.DOMParser).parseFromString(n,"text/html").body;return t.removeChild(t.firstChild),t}catch(e){return null}},n.prototype.getInertBodyElement_InertDocument=function(n){var t=this.inertDocument.createElement("template");return"content"in t?(t.innerHTML=n,t):(this.inertBodyElement.innerHTML=n,this.defaultDoc.documentMode&&this.stripCustomNsAttrs(this.inertBodyElement),this.inertBodyElement)},n.prototype.stripCustomNsAttrs=function(n){for(var t=n.attributes,e=t.length-1;0"),!0},n.prototype.endElement=function(n){var t=n.nodeName.toLowerCase();Oo.hasOwnProperty(t)&&!wo.hasOwnProperty(t)&&(this.buf.push(""))},n.prototype.chars=function(n){this.buf.push(No(n))},n.prototype.checkClobberedElement=function(n,t){if(t&&(n.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)===Node.DOCUMENT_POSITION_CONTAINED_BY)throw new Error("Failed to sanitize html because the element is clobbered: "+n.outerHTML);return t},n}(),Io=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Po=/([^\#-~ |!])/g;function No(n){return n.replace(/&/g,"&").replace(Io,function(n){return"&#"+(1024*(n.charCodeAt(0)-55296)+(n.charCodeAt(1)-56320)+65536)+";"}).replace(Po,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}function Do(n){return"content"in n&&function(n){return n.nodeType===Node.ELEMENT_NODE&&"TEMPLATE"===n.nodeName}(n)?n.content:null}var Mo={provide:Zr,useFactory:function(){return new eo},deps:[]},Vo=function(n){function t(t,e){var r=n.call(this)||this;return r._bootstrapComponents=[],r.destroyCbs=[],r._bootstrapComponents=(t[vn]||null).bootstrap,r.injector=function(n,t,e){return void 0===t&&(t=null),void 0===e&&(e=null),t=t||Dr(),new Mr(n,e,t)}(t,e,[Mo,{provide:qe,useValue:r}]),r.instance=r.injector.get(t),r.componentFactoryResolver=new eo,r}return o(t,n),t.prototype.destroy=function(){this.destroyCbs.forEach(function(n){return n()}),this.destroyCbs=null},t.prototype.onDestroy=function(n){this.destroyCbs.push(n)},t}(qe);!function(n){function t(t){var e=n.call(this)||this;return e.moduleType=t,e}o(t,n),t.prototype.create=function(n){return new Vo(this.moduleType,n)}}($e);var Ho=function(n){function t(t){void 0===t&&(t=!1);var e=n.call(this)||this;return e.__isAsync=t,e}return o(t,n),t.prototype.emit=function(t){n.prototype.next.call(this,t)},t.prototype.subscribe=function(t,e,r){var o,i=function(n){return null},l=function(){return null};t&&"object"==typeof t?(o=this.__isAsync?function(n){setTimeout(function(){return t.next(n)})}:function(n){t.next(n)},t.error&&(i=this.__isAsync?function(n){setTimeout(function(){return t.error(n)})}:function(n){t.error(n)}),t.complete&&(l=this.__isAsync?function(){setTimeout(function(){return t.complete()})}:function(){t.complete()})):(o=this.__isAsync?function(n){setTimeout(function(){return t(n)})}:function(n){t(n)},e&&(i=this.__isAsync?function(n){setTimeout(function(){return e(n)})}:function(n){e(n)}),r&&(l=this.__isAsync?function(){setTimeout(function(){return r()})}:function(){r()}));var u=n.prototype.subscribe.call(this,o,i,l);return t instanceof _&&t.add(u),u},t}(H),Ro=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return jo(n,Qr)},n}(),jo=Ee,Lo=function(n){return n[n.NONE=0]="NONE",n[n.HTML=1]="HTML",n[n.STYLE=2]="STYLE",n[n.SCRIPT=3]="SCRIPT",n[n.URL=4]="URL",n[n.RESOURCE_URL=5]="RESOURCE_URL",n}({}),zo=function(){return function(){}}(),Fo=new RegExp("^([-,.\"'%_!# a-zA-Z0-9]+|(?:(?:matrix|translate|scale|rotate|skew|perspective)(?:X|Y|3d)?|(?:rgb|hsl)a?|(?:repeating-)?(?:linear|radial)-gradient|(?:calc|attr))\\([-0-9.%, #a-zA-Z]+\\))$","g"),Bo=/^url\(([^)]+)\)$/;Function,String,String;var Uo="ngDebugContext",Go="ngOriginalError",Zo="ngErrorLogger";function qo(n){return n[Uo]}function $o(n){return n[Go]}function Qo(n){for(var t=[],e=1;e0&&(o=setTimeout(function(){r._callbacks=r._callbacks.filter(function(n){return n.timeoutId!==o}),n(r._didWork,r.getPendingTasks())},t)),this._callbacks.push({doneCb:n,timeoutId:o,updateCb:e})},n.prototype.whenStable=function(n,t,e){if(e&&!this.taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/dist/task-tracking.js" loaded?');this.addCallback(n,t,e),this._runCallbacksIfReady()},n.prototype.getPendingRequestCount=function(){return this._pendingCount},n.prototype.findProviders=function(n,t,e){return[]},n}(),ki=function(){function n(){this._applications=new Map,Si.addToWindow(this)}return n.prototype.registerApplication=function(n,t){this._applications.set(n,t)},n.prototype.unregisterApplication=function(n){this._applications.delete(n)},n.prototype.unregisterAllApplications=function(){this._applications.clear()},n.prototype.getTestability=function(n){return this._applications.get(n)||null},n.prototype.getAllTestabilities=function(){return Array.from(this._applications.values())},n.prototype.getAllRootElements=function(){return Array.from(this._applications.keys())},n.prototype.findTestabilityInTree=function(n,t){return void 0===t&&(t=!0),Si.findTestabilityInTree(this,n,t)},l([u("design:paramtypes",[])],n)}(),Si=new(function(){function n(){}return n.prototype.addToWindow=function(n){},n.prototype.findTestabilityInTree=function(n,t,e){return null},n}()),Ai=new wn("AllowMultipleToken"),Ti=function(){return function(n,t){this.name=n,this.token=t}}();function Ii(n,t,e){void 0===e&&(e=[]);var r="Platform: "+t,o=new wn(r);return function(t){void 0===t&&(t=[]);var i=Pi();if(!i||i.injector.get(Ai,!1))if(n)n(e.concat(t).concat({provide:o,useValue:!0}));else{var l=e.concat(t).concat({provide:o,useValue:!0});!function(n){if(Ei&&!Ei.destroyed&&!Ei.injector.get(Ai,!1))throw new Error("There can be only one platform. Destroy the previous one to create a new one.");Ei=n.get(Ni);var t=n.get(ri,null);t&&t.forEach(function(n){return n()})}(Ne.create({providers:l,name:r}))}return function(n){var t=Pi();if(!t)throw new Error("No platform exists!");if(!t.injector.get(n,null))throw new Error("A platform with a different configuration has been created. Please destroy it first.");return t}(o)}}function Pi(){return Ei&&!Ei.destroyed?Ei:null}var Ni=function(){function n(n){this._injector=n,this._modules=[],this._destroyListeners=[],this._destroyed=!1}return n.prototype.bootstrapModuleFactory=function(n,t){var e,r=this,o="noop"===(e=t?t.ngZone:void 0)?new xi:("zone.js"===e?void 0:e)||new yi({enableLongStackTrace:ho()}),i=[{provide:yi,useValue:o}];return o.run(function(){var t=Ne.create({providers:i,parent:r.injector,name:n.moduleType.name}),e=n.create(t),l=e.injector.get(Wo,null);if(!l)throw new Error("No ErrorHandler. Is platform module (BrowserModule) included?");return e.onDestroy(function(){return Vi(r._modules,e)}),o.runOutsideAngular(function(){return o.onError.subscribe({next:function(n){l.handleError(n)}})}),function(n,t,o){try{var i=((l=e.injector.get(Xo)).runInitializers(),l.donePromise.then(function(){return r._moduleDoBootstrap(e),e}));return Ko(i)?i.catch(function(e){throw t.runOutsideAngular(function(){return n.handleError(e)}),e}):i}catch(u){throw t.runOutsideAngular(function(){return n.handleError(u)}),u}var l}(l,o)})},n.prototype.bootstrapModule=function(n,t){var e=this;void 0===t&&(t=[]);var r=Di({},t);return function(n,t,e){return n.get(fi).createCompiler([t]).compileModuleAsync(e)}(this.injector,r,n).then(function(n){return e.bootstrapModuleFactory(n,r)})},n.prototype._moduleDoBootstrap=function(n){var t=n.injector.get(Mi);if(n._bootstrapComponents.length>0)n._bootstrapComponents.forEach(function(n){return t.bootstrap(n)});else{if(!n.instance.ngDoBootstrap)throw new Error("The module "+Dn(n.instance.constructor)+' was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. Please define one of these.');n.instance.ngDoBootstrap(t)}this._modules.push(n)},n.prototype.onDestroy=function(n){this._destroyListeners.push(n)},Object.defineProperty(n.prototype,"injector",{get:function(){return this._injector},enumerable:!0,configurable:!0}),n.prototype.destroy=function(){if(this._destroyed)throw new Error("The platform has already been destroyed!");this._modules.slice().forEach(function(n){return n.destroy()}),this._destroyListeners.forEach(function(n){return n()}),this._destroyed=!0},Object.defineProperty(n.prototype,"destroyed",{get:function(){return this._destroyed},enumerable:!0,configurable:!0}),n}();function Di(n,t){return Array.isArray(t)?t.reduce(Di,n):i({},n,t)}var Mi=function(){function n(n,t,e,r,o,i){var l=this;this._zone=n,this._console=t,this._injector=e,this._exceptionHandler=r,this._componentFactoryResolver=o,this._initStatus=i,this._bootstrapListeners=[],this._views=[],this._runningTick=!1,this._enforceNoNewChanges=!1,this._stable=!0,this.componentTypes=[],this.components=[],this._enforceNoNewChanges=ho(),this._zone.onMicrotaskEmpty.subscribe({next:function(){l._zone.run(function(){l.tick()})}});var u=new I(function(n){l._stable=l._zone.isStable&&!l._zone.hasPendingMacrotasks&&!l._zone.hasPendingMicrotasks,l._zone.runOutsideAngular(function(){n.next(l._stable),n.complete()})}),s=new I(function(n){var t;l._zone.runOutsideAngular(function(){t=l._zone.onStable.subscribe(function(){yi.assertNotInAngularZone(),Pn(function(){l._stable||l._zone.hasPendingMacrotasks||l._zone.hasPendingMicrotasks||(l._stable=!0,n.next(!0))})})});var e=l._zone.onUnstable.subscribe(function(){yi.assertInAngularZone(),l._stable&&(l._stable=!1,l._zone.runOutsideAngular(function(){n.next(!1)}))});return function(){t.unsubscribe(),e.unsubscribe()}});this.isStable=function(){for(var n=[],t=0;t1&&"number"==typeof n[n.length-1]&&(r=n.pop())):"number"==typeof i&&(r=n.pop()),null===o&&1===n.length&&n[0]instanceof I?n[0]:function(n){return void 0===n&&(n=Number.POSITIVE_INFINITY),function n(t,e,r){return void 0===r&&(r=Number.POSITIVE_INFINITY),"function"==typeof e?function(o){return o.pipe(n(function(n,r){return nn(t(n,r)).pipe(K(function(t,o){return e(n,t,r,o)}))},r))}:("number"==typeof e&&(r=e),function(n){return n.lift(new tn(t,r))})}(rn,n)}(r)(X(n,o))}(u,s.pipe(function(n){return on()((t=fn,function(n){var e;e="function"==typeof t?t:function(){return t};var r=Object.create(n,an);return r.source=n,r.subjectFactory=e,r})(n));var t}))}var t;return t=n,n.prototype.bootstrap=function(n,t){var e,r=this;if(!this._initStatus.done)throw new Error("Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.");e=n instanceof Fr?n:this._componentFactoryResolver.resolveComponentFactory(n),this.componentTypes.push(e.componentType);var o=e instanceof $r?null:this._injector.get(qe),i=e.create(Ne.NULL,[],t||e.selector,o);i.onDestroy(function(){r._unloadComponent(i)});var l=i.injector.get(Oi,null);return l&&i.injector.get(ki).registerApplication(i.location.nativeElement,l),this._loadComponent(i),ho()&&this._console.log("Angular is running in the development mode. Call enableProdMode() to enable the production mode."),i},n.prototype.tick=function(){var n=this;if(this._runningTick)throw new Error("ApplicationRef.tick is called recursively");var e=t._tickScope();try{this._runningTick=!0,this._views.forEach(function(n){return n.detectChanges()}),this._enforceNoNewChanges&&this._views.forEach(function(n){return n.checkNoChanges()})}catch(r){this._zone.runOutsideAngular(function(){return n._exceptionHandler.handleError(r)})}finally{this._runningTick=!1,vi(e)}},n.prototype.attachView=function(n){var t=n;this._views.push(t),t.attachToAppRef(this)},n.prototype.detachView=function(n){var t=n;Vi(this._views,t),t.detachFromAppRef()},n.prototype._loadComponent=function(n){this.attachView(n.hostView),this.tick(),this.components.push(n),this._injector.get(ii,[]).concat(this._bootstrapListeners).forEach(function(t){return t(n)})},n.prototype._unloadComponent=function(n){this.detachView(n.hostView),Vi(this.components,n)},n.prototype.ngOnDestroy=function(){this._views.slice().forEach(function(n){return n.destroy()})},Object.defineProperty(n.prototype,"viewCount",{get:function(){return this._views.length},enumerable:!0,configurable:!0}),n._tickScope=gi("ApplicationRef#tick()"),n}();function Vi(n,t){var e=n.indexOf(t);e>-1&&n.splice(e,1)}var Hi,Ri=function(){function n(){this.dirty=!0,this._results=[],this.changes=new Ho,this.length=0}return n.prototype.map=function(n){return this._results.map(n)},n.prototype.filter=function(n){return this._results.filter(n)},n.prototype.find=function(n){return this._results.find(n)},n.prototype.reduce=function(n,t){return this._results.reduce(n,t)},n.prototype.forEach=function(n){this._results.forEach(n)},n.prototype.some=function(n){return this._results.some(n)},n.prototype.toArray=function(){return this._results.slice()},n.prototype[In()]=function(){return this._results[In()]()},n.prototype.toString=function(){return this._results.toString()},n.prototype.reset=function(n){this._results=function n(t){return t.reduce(function(t,e){var r=Array.isArray(e)?n(e):e;return t.concat(r)},[])}(n),this.dirty=!1,this.length=this._results.length,this.last=this._results[this.length-1],this.first=this._results[0]},n.prototype.notifyOnChanges=function(){this.changes.emit(this)},n.prototype.setDirty=function(){this.dirty=!0},n.prototype.destroy=function(){this.changes.complete(),this.changes.unsubscribe()},n}(),ji=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Li(n,Qr)},n}(),Li=Ee,zi=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Fi()},n}(),Fi=function(){for(var n=[],t=0;t-1}(r)||"root"===o.providedIn&&r._def.isRoot))){var c=n._providers.length;return n._def.providersByKey[t.tokenKey]={flags:5120,value:u.factory,deps:[],index:c,token:t.token},n._providers[c]=fu,n._providers[c]=yu(n,n._def.providersByKey[t.tokenKey])}return 4&t.flags?e:n._parent.get(t.token,e)}finally{Fn(i)}}function yu(n,t){var e;switch(201347067&t.flags){case 512:e=function(n,t,e){var r=e.length;switch(r){case 0:return new t;case 1:return new t(vu(n,e[0]));case 2:return new t(vu(n,e[0]),vu(n,e[1]));case 3:return new t(vu(n,e[0]),vu(n,e[1]),vu(n,e[2]));default:for(var o=new Array(r),i=0;i=e.length)&&(t=e.length-1),t<0)return null;var r=e[t];return r.viewContainerParent=null,Cu(e,t),Cl.dirtyParentQueries(r),_u(r),r}function bu(n,t,e){var r=t?Fl(t,t.def.lastRenderRootNode):n.renderElement,o=e.renderer.parentNode(r),i=e.renderer.nextSibling(r);Wl(e,2,o,i,void 0)}function _u(n){Wl(n,3,null,null,void 0)}function wu(n,t,e){t>=n.length?n.push(e):n.splice(t,0,e)}function Cu(n,t){t>=n.length-1?n.pop():n.splice(t,1)}var Eu=new Object;function xu(n,t,e,r,o,i){return new Ou(n,t,e,r,o,i)}var Ou=function(n){function t(t,e,r,o,i,l){var u=n.call(this)||this;return u.selector=t,u.componentType=e,u._inputs=o,u._outputs=i,u.ngContentSelectors=l,u.viewDefFactory=r,u}return o(t,n),Object.defineProperty(t.prototype,"inputs",{get:function(){var n=[],t=this._inputs;for(var e in t)n.push({propName:e,templateName:t[e]});return n},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"outputs",{get:function(){var n=[];for(var t in this._outputs)n.push({propName:t,templateName:this._outputs[t]});return n},enumerable:!0,configurable:!0}),t.prototype.create=function(n,t,e,r){if(!r)throw new Error("ngModule should be provided");var o=Ql(this.viewDefFactory),i=o.nodes[0].element.componentProvider.nodeIndex,l=Cl.createRootView(n,t||[],e,o,r,Eu),u=bl(l,i).instance;return e&&l.renderer.setAttribute(ml(l,0).renderElement,"ng-version",to.full),new ku(l,new Iu(l),u)},t}(Fr),ku=function(n){function t(t,e,r){var o=n.call(this)||this;return o._view=t,o._viewRef=e,o._component=r,o._elDef=o._view.def.nodes[0],o.hostView=e,o.changeDetectorRef=e,o.instance=r,o}return o(t,n),Object.defineProperty(t.prototype,"location",{get:function(){return new Qr(ml(this._view,this._elDef.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"componentType",{get:function(){return this._component.constructor},enumerable:!0,configurable:!0}),t.prototype.destroy=function(){this._viewRef.destroy()},t.prototype.onDestroy=function(n){this._viewRef.onDestroy(n)},t}(zr);function Su(n,t,e){return new Au(n,t,e)}var Au=function(){function n(n,t,e){this._view=n,this._elDef=t,this._data=e,this._embeddedViews=[]}return Object.defineProperty(n.prototype,"element",{get:function(){return new Qr(this._data.renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"parentInjector",{get:function(){for(var n=this._view,t=this._elDef.parent;!t&&n;)t=zl(n),n=n.parent;return n?new Mu(n,t):new Mu(this._view,null)},enumerable:!0,configurable:!0}),n.prototype.clear=function(){for(var n=this._embeddedViews.length-1;n>=0;n--){var t=mu(this._data,n);Cl.destroyView(t)}},n.prototype.get=function(n){var t=this._embeddedViews[n];if(t){var e=new Iu(t);return e.attachToViewContainerRef(this),e}return null},Object.defineProperty(n.prototype,"length",{get:function(){return this._embeddedViews.length},enumerable:!0,configurable:!0}),n.prototype.createEmbeddedView=function(n,t,e){var r=n.createEmbeddedView(t||{});return this.insert(r,e),r},n.prototype.createComponent=function(n,t,e,r,o){var i=e||this.parentInjector;o||n instanceof $r||(o=i.get(qe));var l=n.create(i,r,void 0,o);return this.insert(l.hostView,t),l},n.prototype.insert=function(n,t){if(n.destroyed)throw new Error("Cannot insert a destroyed View in a ViewContainer!");var e,r,o,i,l=n;return i=(e=this._data).viewContainer._embeddedViews,null==(r=t)&&(r=i.length),(o=l._view).viewContainerParent=this._view,wu(i,r,o),function(n,t){var e=Ll(t);if(e&&e!==n&&!(16&t.state)){t.state|=16;var r=e.template._projectedViews;r||(r=e.template._projectedViews=[]),r.push(t),function(n,e){if(!(4&e.flags)){t.parent.def.nodeFlags|=4,e.flags|=4;for(var r=e.parent;r;)r.childFlags|=4,r=r.parent}}(0,t.parentNodeDef)}}(e,o),Cl.dirtyParentQueries(o),bu(e,r>0?i[r-1]:null,o),l.attachToViewContainerRef(this),n},n.prototype.move=function(n,t){if(n.destroyed)throw new Error("Cannot move a destroyed View in a ViewContainer!");var e,r,o,i,l,u=this._embeddedViews.indexOf(n._view);return o=t,l=(i=(e=this._data).viewContainer._embeddedViews)[r=u],Cu(i,r),null==o&&(o=i.length),wu(i,o,l),Cl.dirtyParentQueries(l),_u(l),bu(e,o>0?i[o-1]:null,l),n},n.prototype.indexOf=function(n){return this._embeddedViews.indexOf(n._view)},n.prototype.remove=function(n){var t=mu(this._data,n);t&&Cl.destroyView(t)},n.prototype.detach=function(n){var t=mu(this._data,n);return t?new Iu(t):null},n}();function Tu(n){return new Iu(n)}var Iu=function(){function n(n){this._view=n,this._viewContainerRef=null,this._appRef=null}return Object.defineProperty(n.prototype,"rootNodes",{get:function(){return Wl(this._view,0,void 0,void 0,n=[]),n;var n},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"context",{get:function(){return this._view.context},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"destroyed",{get:function(){return 0!=(128&this._view.state)},enumerable:!0,configurable:!0}),n.prototype.markForCheck=function(){Hl(this._view)},n.prototype.detach=function(){this._view.state&=-5},n.prototype.detectChanges=function(){var n=this._view.root.rendererFactory;n.begin&&n.begin();try{Cl.checkAndUpdateView(this._view)}finally{n.end&&n.end()}},n.prototype.checkNoChanges=function(){Cl.checkNoChangesView(this._view)},n.prototype.reattach=function(){this._view.state|=4},n.prototype.onDestroy=function(n){this._view.disposables||(this._view.disposables=[]),this._view.disposables.push(n)},n.prototype.destroy=function(){this._appRef?this._appRef.detachView(this):this._viewContainerRef&&this._viewContainerRef.detach(this._viewContainerRef.indexOf(this)),Cl.destroyView(this._view)},n.prototype.detachFromAppRef=function(){this._appRef=null,_u(this._view),Cl.dirtyParentQueries(this._view)},n.prototype.attachToAppRef=function(n){if(this._viewContainerRef)throw new Error("This view is already attached to a ViewContainer!");this._appRef=n},n.prototype.attachToViewContainerRef=function(n){if(this._appRef)throw new Error("This view is already attached directly to the ApplicationRef!");this._viewContainerRef=n},n}();function Pu(n,t){return new Nu(n,t)}var Nu=function(n){function t(t,e){var r=n.call(this)||this;return r._parentView=t,r._def=e,r}return o(t,n),t.prototype.createEmbeddedView=function(n){return new Iu(Cl.createEmbeddedView(this._parentView,this._def,this._def.element.template,n))},Object.defineProperty(t.prototype,"elementRef",{get:function(){return new Qr(ml(this._parentView,this._def.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),t}(Ro);function Du(n,t){return new Mu(n,t)}var Mu=function(){function n(n,t){this.view=n,this.elDef=t}return n.prototype.get=function(n,t){return void 0===t&&(t=Ne.THROW_IF_NOT_FOUND),Cl.resolveDep(this.view,this.elDef,!!this.elDef&&0!=(33554432&this.elDef.flags),{flags:0,token:n,tokenKey:Al(n)},t)},n}();function Vu(n,t){var e=n.def.nodes[t];if(1&e.flags){var r=ml(n,e.nodeIndex);return e.element.template?r.template:r.renderElement}if(2&e.flags)return yl(n,e.nodeIndex).renderText;if(20240&e.flags)return bl(n,e.nodeIndex).instance;throw new Error("Illegal state: read nodeValue for node index "+t)}function Hu(n){return new Ru(n.renderer)}var Ru=function(){function n(n){this.delegate=n}return n.prototype.selectRootElement=function(n){return this.delegate.selectRootElement(n)},n.prototype.createElement=function(n,t){var e=a(tu(t),2),r=this.delegate.createElement(e[1],e[0]);return n&&this.delegate.appendChild(n,r),r},n.prototype.createViewRoot=function(n){return n},n.prototype.createTemplateAnchor=function(n){var t=this.delegate.createComment("");return n&&this.delegate.appendChild(n,t),t},n.prototype.createText=function(n,t){var e=this.delegate.createText(t);return n&&this.delegate.appendChild(n,e),e},n.prototype.projectNodes=function(n,t){for(var e=0;e0,t.provider.value,t.provider.deps);if(t.outputs.length)for(var r=0;r0,r=t.provider;switch(201347067&t.flags){case 512:return es(n,t.parent,e,r.value,r.deps);case 1024:return function(n,t,e,r,o){var i=o.length;switch(i){case 0:return r();case 1:return r(os(n,t,e,o[0]));case 2:return r(os(n,t,e,o[0]),os(n,t,e,o[1]));case 3:return r(os(n,t,e,o[0]),os(n,t,e,o[1]),os(n,t,e,o[2]));default:for(var l=Array(i),u=0;u0)a=g,_s(g)||(c=g);else for(;a&&d===a.nodeIndex+a.childCount;){var m=a.parent;m&&(m.childFlags|=a.childFlags,m.childMatchedQueries|=a.childMatchedQueries),c=(a=m)&&_s(a)?a.renderParent:a}}return{factory:null,nodeFlags:l,rootNodeFlags:u,nodeMatchedQueries:s,flags:n,nodes:t,updateDirectives:e||kl,updateRenderer:r||kl,handleEvent:function(n,e,r,o){return t[e].element.handleEvent(n,r,o)},bindingCount:o,outputCount:i,lastRenderRootNode:p}}function _s(n){return 0!=(1&n.flags)&&null===n.element.name}function ws(n,t,e){var r=t.element&&t.element.template;if(r){if(!r.lastRenderRootNode)throw new Error("Illegal State: Embedded templates without nodes are not allowed!");if(r.lastRenderRootNode&&16777216&r.lastRenderRootNode.flags)throw new Error("Illegal State: Last root node of a template can't have embedded views, at index "+t.nodeIndex+"!")}if(20224&t.flags&&0==(1&(n?n.flags:0)))throw new Error("Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index "+t.nodeIndex+"!");if(t.query){if(67108864&t.flags&&(!n||0==(16384&n.flags)))throw new Error("Illegal State: Content Query nodes need to be children of directives, at index "+t.nodeIndex+"!");if(134217728&t.flags&&n)throw new Error("Illegal State: View Query nodes have to be top level nodes, at index "+t.nodeIndex+"!")}if(t.childCount){var o=n?n.nodeIndex+n.childCount:e-1;if(t.nodeIndex<=o&&t.nodeIndex+t.childCount>o)throw new Error("Illegal State: childCount of node leads outside of parent, at index "+t.nodeIndex+"!")}}function Cs(n,t,e,r){var o=Os(n.root,n.renderer,n,t,e);return ks(o,n.component,r),Ss(o),o}function Es(n,t,e){var r=Os(n,n.renderer,null,null,t);return ks(r,e,e),Ss(r),r}function xs(n,t,e,r){var o,i=t.element.componentRendererType;return o=i?n.root.rendererFactory.createRenderer(r,i):n.root.renderer,Os(n.root,o,n,t.element.componentProvider,e)}function Os(n,t,e,r,o){var i=new Array(o.nodes.length),l=o.outputCount?new Array(o.outputCount):null;return{def:o,parent:e,viewContainerParent:null,parentNodeDef:r,context:null,component:null,nodes:i,state:13,root:n,renderer:t,oldValues:new Array(o.bindingCount),disposables:l,initIndex:-1}}function ks(n,t,e){n.component=t,n.context=e}function Ss(n){var t;Bl(n)&&(t=ml(n.parent,n.parentNodeDef.parent.nodeIndex).renderElement);for(var e=n.def,r=n.nodes,o=0;o0&&cu(n,t,0,e)&&(p=!0),h>1&&cu(n,t,1,r)&&(p=!0),h>2&&cu(n,t,2,o)&&(p=!0),h>3&&cu(n,t,3,i)&&(p=!0),h>4&&cu(n,t,4,l)&&(p=!0),h>5&&cu(n,t,5,u)&&(p=!0),h>6&&cu(n,t,6,s)&&(p=!0),h>7&&cu(n,t,7,a)&&(p=!0),h>8&&cu(n,t,8,c)&&(p=!0),h>9&&cu(n,t,9,f)&&(p=!0),p}(n,t,e,r,o,i,l,u,s,a,c,f);case 2:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=!1,p=t.bindings,d=p.length;if(d>0&&Ml(n,t,0,e)&&(h=!0),d>1&&Ml(n,t,1,r)&&(h=!0),d>2&&Ml(n,t,2,o)&&(h=!0),d>3&&Ml(n,t,3,i)&&(h=!0),d>4&&Ml(n,t,4,l)&&(h=!0),d>5&&Ml(n,t,5,u)&&(h=!0),d>6&&Ml(n,t,6,s)&&(h=!0),d>7&&Ml(n,t,7,a)&&(h=!0),d>8&&Ml(n,t,8,c)&&(h=!0),d>9&&Ml(n,t,9,f)&&(h=!0),h){var g=t.text.prefix;d>0&&(g+=ms(e,p[0])),d>1&&(g+=ms(r,p[1])),d>2&&(g+=ms(o,p[2])),d>3&&(g+=ms(i,p[3])),d>4&&(g+=ms(l,p[4])),d>5&&(g+=ms(u,p[5])),d>6&&(g+=ms(s,p[6])),d>7&&(g+=ms(a,p[7])),d>8&&(g+=ms(c,p[8])),d>9&&(g+=ms(f,p[9]));var v=yl(n,t.nodeIndex).renderText;n.renderer.setValue(v,g)}return h}(n,t,e,r,o,i,l,u,s,a,c,f);case 16384:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=bl(n,t.nodeIndex),p=h.instance,d=!1,g=void 0,v=t.bindings.length;return v>0&&Dl(n,t,0,e)&&(d=!0,g=ls(n,h,t,0,e,g)),v>1&&Dl(n,t,1,r)&&(d=!0,g=ls(n,h,t,1,r,g)),v>2&&Dl(n,t,2,o)&&(d=!0,g=ls(n,h,t,2,o,g)),v>3&&Dl(n,t,3,i)&&(d=!0,g=ls(n,h,t,3,i,g)),v>4&&Dl(n,t,4,l)&&(d=!0,g=ls(n,h,t,4,l,g)),v>5&&Dl(n,t,5,u)&&(d=!0,g=ls(n,h,t,5,u,g)),v>6&&Dl(n,t,6,s)&&(d=!0,g=ls(n,h,t,6,s,g)),v>7&&Dl(n,t,7,a)&&(d=!0,g=ls(n,h,t,7,a,g)),v>8&&Dl(n,t,8,c)&&(d=!0,g=ls(n,h,t,8,c,g)),v>9&&Dl(n,t,9,f)&&(d=!0,g=ls(n,h,t,9,f,g)),g&&p.ngOnChanges(g),65536&t.flags&&vl(n,256,t.nodeIndex)&&p.ngOnInit(),262144&t.flags&&p.ngDoCheck(),d}(n,t,e,r,o,i,l,u,s,a,c,f);case 32:case 64:case 128:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=t.bindings,p=!1,d=h.length;if(d>0&&Ml(n,t,0,e)&&(p=!0),d>1&&Ml(n,t,1,r)&&(p=!0),d>2&&Ml(n,t,2,o)&&(p=!0),d>3&&Ml(n,t,3,i)&&(p=!0),d>4&&Ml(n,t,4,l)&&(p=!0),d>5&&Ml(n,t,5,u)&&(p=!0),d>6&&Ml(n,t,6,s)&&(p=!0),d>7&&Ml(n,t,7,a)&&(p=!0),d>8&&Ml(n,t,8,c)&&(p=!0),d>9&&Ml(n,t,9,f)&&(p=!0),p){var g=_l(n,t.nodeIndex),v=void 0;switch(201347067&t.flags){case 32:v=new Array(h.length),d>0&&(v[0]=e),d>1&&(v[1]=r),d>2&&(v[2]=o),d>3&&(v[3]=i),d>4&&(v[4]=l),d>5&&(v[5]=u),d>6&&(v[6]=s),d>7&&(v[7]=a),d>8&&(v[8]=c),d>9&&(v[9]=f);break;case 64:v={},d>0&&(v[h[0].name]=e),d>1&&(v[h[1].name]=r),d>2&&(v[h[2].name]=o),d>3&&(v[h[3].name]=i),d>4&&(v[h[4].name]=l),d>5&&(v[h[5].name]=u),d>6&&(v[h[6].name]=s),d>7&&(v[h[7].name]=a),d>8&&(v[h[8].name]=c),d>9&&(v[h[9].name]=f);break;case 128:var y=e;switch(d){case 1:v=y.transform(e);break;case 2:v=y.transform(r);break;case 3:v=y.transform(r,o);break;case 4:v=y.transform(r,o,i);break;case 5:v=y.transform(r,o,i,l);break;case 6:v=y.transform(r,o,i,l,u);break;case 7:v=y.transform(r,o,i,l,u,s);break;case 8:v=y.transform(r,o,i,l,u,s,a);break;case 9:v=y.transform(r,o,i,l,u,s,a,c);break;case 10:v=y.transform(r,o,i,l,u,s,a,c,f)}}g.value=v}return p}(n,t,e,r,o,i,l,u,s,a,c,f);default:throw"unreachable"}}(n,t,r,o,i,l,u,s,a,f,h,p):function(n,t,e){switch(201347067&t.flags){case 1:return function(n,t,e){for(var r=!1,o=0;o0&&Vl(n,t,0,e),h>1&&Vl(n,t,1,r),h>2&&Vl(n,t,2,o),h>3&&Vl(n,t,3,i),h>4&&Vl(n,t,4,l),h>5&&Vl(n,t,5,u),h>6&&Vl(n,t,6,s),h>7&&Vl(n,t,7,a),h>8&&Vl(n,t,8,c),h>9&&Vl(n,t,9,f)}(n,t,r,o,i,l,u,s,a,c,f,h):function(n,t,e){for(var r=0;r0){var i=new Set(n.modules);Ws.forEach(function(t,r){if(i.has(bn(r).providedIn)){var o={token:r,flags:t.flags|(e?4096:0),deps:Zl(t.deps),value:t.value,index:n.providers.length};n.providers.push(o),n.providersByKey[Al(r)]=o}})}}(n=n.factory(function(){return kl})),n):n}(r))}var Qs=new Map,Ws=new Map,Ks=new Map;function Js(n){var t;Qs.set(n.token,n),"function"==typeof n.token&&(t=bn(n.token))&&"function"==typeof t.providedIn&&Ws.set(n.token,n)}function Ys(n,t){var e=Ql(t.viewDefFactory),r=Ql(e.nodes[0].element.componentView);Ks.set(n,r)}function Xs(){Qs.clear(),Ws.clear(),Ks.clear()}function na(n){if(0===Qs.size)return n;var t=function(n){for(var t=[],e=null,r=0;r=this.currentHistoricCoverage.lcq)return!1}else if("branchCoverageIncreaseOnly"===t){var r=this.branchCoverage;if(isNaN(r)||r<=this.currentHistoricCoverage.bcq)return!1}else if("branchCoverageDecreaseOnly"===t&&(r=this.branchCoverage,isNaN(r)||r>=this.currentHistoricCoverage.bcq))return!1;return!0},t.prototype.updateCurrentHistoricCoverage=function(n){if(this.currentHistoricCoverage=null,""!==n)for(var t=0;t-1&&null===e,r}return o(t,n),t.prototype.visible=function(n,t){if(""!==n&&this.name.toLowerCase().indexOf(n.toLowerCase())>-1)return!0;for(var e=0;et&&(r[o].collapsed=n.settings.collapseStates[t]),t++,e(r[o].subElements)};e(this.codeElements)},n}(),La=new I(function(n){return n.complete()}),za=function(n){function t(t,e){var r=n.call(this,t)||this;r.sources=e,r.completed=0,r.haveValues=0;var o=e.length;r.values=new Array(o);for(var i=0;i0},t.prototype.tagName=function(n){return n.tagName},t.prototype.attributeMap=function(n){for(var t=new Map,e=n.attributes,r=0;r0;l||(l=n[i]=[]);var s=$c(t)?Zone.root:Zone.current;if(0===l.length)l.push({zone:s,handler:o});else{for(var a=!1,c=0;c-1},t}(kc),tf=["alt","control","meta","shift"],ef={alt:function(n){return n.altKey},control:function(n){return n.ctrlKey},meta:function(n){return n.metaKey},shift:function(n){return n.shiftKey}},rf=function(n){function t(t){return n.call(this,t)||this}var e;return o(t,n),e=t,t.prototype.supports=function(n){return null!=e.parseEventName(n)},t.prototype.addEventListener=function(n,t,r){var o=e.parseEventName(t),i=e.eventCallback(o.fullKey,r,this.manager.getZone());return this.manager.getZone().runOutsideAngular(function(){return uc().onAndCancel(n,o.domEventName,i)})},t.parseEventName=function(n){var t=n.toLowerCase().split("."),r=t.shift();if(0===t.length||"keydown"!==r&&"keyup"!==r)return null;var o=e._normalizeKey(t.pop()),i="";if(tf.forEach(function(n){var e=t.indexOf(n);e>-1&&(t.splice(e,1),i+=n+".")}),i+=o,0!=t.length||0===o.length)return null;var l={};return l.domEventName=r,l.fullKey=i,l},t.getEventFullKey=function(n){var t="",e=uc().getEventKey(n);return" "===(e=e.toLowerCase())?e="space":"."===e&&(e="dot"),tf.forEach(function(r){r!=e&&(0,ef[r])(n)&&(t+=r+".")}),t+=e},t.eventCallback=function(n,t,r){return function(o){e.getEventFullKey(o)===n&&r.runGuarded(function(){return t(o)})}},t._normalizeKey=function(n){switch(n){case"esc":return"escape";default:return n}},t}(kc),of=function(){return function(){}}(),lf=function(n){function t(t){var e=n.call(this)||this;return e._doc=t,e}return o(t,n),t.prototype.sanitize=function(n,t){if(null==t)return null;switch(n){case Lo.NONE:return t;case Lo.HTML:return t instanceof sf?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"HTML"),function(n,t){var e=null;try{_o=_o||new po(n);var r=t?String(t):"";e=_o.getInertBodyElement(r);var o=5,i=r;do{if(0===o)throw new Error("Failed to sanitize html because the input is unstable");o--,r=i,i=e.innerHTML,e=_o.getInertBodyElement(r)}while(r!==i);var l=new To,u=l.sanitizeChildren(Do(e)||e);return ho()&&l.sanitizedSomething&&console.warn("WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."),u}finally{if(e)for(var s=Do(e)||e;s.firstChild;)s.removeChild(s.firstChild)}}(this._doc,String(t)));case Lo.STYLE:return t instanceof af?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"Style"),function(n){if(!(n=String(n).trim()))return"";var t=n.match(Bo);return t&&yo(t[1])===t[1]||n.match(Fo)&&function(n){for(var t=!0,e=!0,r=0;rn?{max:{max:n,actual:t.value}}:null}},n.required=function(n){return mf(n.value)?{required:!0}:null},n.requiredTrue=function(n){return!0===n.value?null:{required:!0}},n.email=function(n){return mf(n.value)?null:bf.test(n.value)?null:{email:!0}},n.minLength=function(n){return function(t){if(mf(t.value))return null;var e=t.value?t.value.length:0;return en?{maxlength:{requiredLength:n,actualLength:e}}:null}},n.pattern=function(t){return t?("string"==typeof t?(r="","^"!==t.charAt(0)&&(r+="^"),r+=t,"$"!==t.charAt(t.length-1)&&(r+="$"),e=new RegExp(r)):(r=t.toString(),e=t),function(n){if(mf(n.value))return null;var t=n.value;return e.test(t)?null:{pattern:{requiredPattern:r,actualValue:t}}}):n.nullValidator;var e,r},n.nullValidator=function(n){return null},n.compose=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return Ef(function(n,e){return t.map(function(t){return t(n)})}(n))}},n.composeAsync=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return function n(){for(var t,e=[],r=0;r=0;--t)if(this._accessors[t][1]===n)return void this._accessors.splice(t,1)},n.prototype.select=function(n){var t=this;this._accessors.forEach(function(e){t._isSameGroup(e,n)&&e[1]!==n&&e[1].fireUncheck(n.value)})},n.prototype._isSameGroup=function(n,t){return!!n[0].control&&n[0]._parent===t._control._parent&&n[1].name===t.name},n}(),Mf=function(){function n(n,t,e,r){this._renderer=n,this._elementRef=t,this._registry=e,this._injector=r,this.onChange=function(){},this.onTouched=function(){}}return n.prototype.ngOnInit=function(){this._control=this._injector.get(Nf),this._checkName(),this._registry.add(this._control,this)},n.prototype.ngOnDestroy=function(){this._registry.remove(this)},n.prototype.writeValue=function(n){this._state=n===this.value,this._renderer.setProperty(this._elementRef.nativeElement,"checked",this._state)},n.prototype.registerOnChange=function(n){var t=this;this._fn=n,this.onChange=function(){n(t.value),t._registry.select(t)}},n.prototype.fireUncheck=function(n){this.writeValue(n)},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._checkName=function(){this.name&&this.formControlName&&this.name!==this.formControlName&&this._throwNameError(),!this.name&&this.formControlName&&(this.name=this.formControlName)},n.prototype._throwNameError=function(){throw new Error('\n If you define both a name and a formControlName attribute on your radio button, their values\n must match. Ex: \n ')},n}(),Vf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this.onChange=function(n){},this.onTouched=function(){}}return n.prototype.writeValue=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"value",parseFloat(n))},n.prototype.registerOnChange=function(n){this.onChange=function(t){n(""==t?null:parseFloat(t))}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n}(),Hf='\n

\n
\n \n
\n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n person: new FormGroup({ firstName: new FormControl() })\n });',Rf='\n
\n
\n \n
\n
';function jf(n,t){return null==n?""+t:(t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Lf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){this.value=n;var t=this._getOptionId(n);null==t&&this._renderer.setProperty(this._elementRef.nativeElement,"selectedIndex",-1);var e=jf(t,n);this._renderer.setProperty(this._elementRef.nativeElement,"value",e)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){t.value=t._getOptionValue(e),n(t.value)}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._registerOption=function(){return(this._idCounter++).toString()},n.prototype._getOptionId=function(n){var t,e;try{for(var r=s(Array.from(this._optionMap.keys())),o=r.next();!o.done;o=r.next()){var i=o.value;if(this._compareWith(this._optionMap.get(i),n))return i}}catch(l){t={error:l}}finally{try{o&&!o.done&&(e=r.return)&&e.call(r)}finally{if(t)throw t.error}}return null},n.prototype._getOptionValue=function(n){var t=function(n){return n.split(":")[0]}(n);return this._optionMap.has(t)?this._optionMap.get(t):n},n}(),zf=function(){function n(n,t,e){this._element=n,this._renderer=t,this._select=e,this._select&&(this.id=this._select._registerOption())}return Object.defineProperty(n.prototype,"ngValue",{set:function(n){null!=this._select&&(this._select._optionMap.set(this.id,n),this._setElementValue(jf(this.id,n)),this._select.writeValue(this._select.value))},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"value",{set:function(n){this._setElementValue(n),this._select&&this._select.writeValue(this._select.value)},enumerable:!0,configurable:!0}),n.prototype._setElementValue=function(n){this._renderer.setProperty(this._element.nativeElement,"value",n)},n.prototype.ngOnDestroy=function(){this._select&&(this._select._optionMap.delete(this.id),this._select.writeValue(this._select.value))},n}();function Ff(n,t){return null==n?""+t:("string"==typeof t&&(t="'"+t+"'"),t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Bf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){var t,e=this;if(this.value=n,Array.isArray(n)){var r=n.map(function(n){return e._getOptionId(n)});t=function(n,t){n._setSelected(r.indexOf(t.toString())>-1)}}else t=function(n,t){n._setSelected(!1)};this._optionMap.forEach(t)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){var r=[];if(e.hasOwnProperty("selectedOptions"))for(var o=e.selectedOptions,i=0;i1?"path: '"+n.path.join(" -> ")+"'":n.path[0]?"name: '"+n.path+"'":"unspecified name attribute",new Error(t+" "+e)}function Qf(n){return null!=n?_f.compose(n.map(Af)):null}function Wf(n){return null!=n?_f.composeAsync(n.map(Tf)):null}var Kf=[Of,Vf,If,Lf,Bf,Mf],Jf=function(n){function t(){return null!==n&&n.apply(this,arguments)||this}return o(t,n),t.prototype.ngOnInit=function(){this._checkParentType(),this.formDirective.addFormGroup(this)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeFormGroup(this)},Object.defineProperty(t.prototype,"control",{get:function(){return this.formDirective.getFormGroup(this)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return Gf(this.name,this._parent)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._validators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._asyncValidators)},enumerable:!0,configurable:!0}),t.prototype._checkParentType=function(){},t}(yf),Yf=function(n){function t(t){return n.call(this,t)||this}return o(t,n),t}(function(){function n(n){this._cd=n}return Object.defineProperty(n.prototype,"ngClassUntouched",{get:function(){return!!this._cd.control&&this._cd.control.untouched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassTouched",{get:function(){return!!this._cd.control&&this._cd.control.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPristine",{get:function(){return!!this._cd.control&&this._cd.control.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassDirty",{get:function(){return!!this._cd.control&&this._cd.control.dirty},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassValid",{get:function(){return!!this._cd.control&&this._cd.control.valid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassInvalid",{get:function(){return!!this._cd.control&&this._cd.control.invalid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPending",{get:function(){return!!this._cd.control&&this._cd.control.pending},enumerable:!0,configurable:!0}),n}());function Xf(n){var t=th(n)?n.validators:n;return Array.isArray(t)?Qf(t):t||null}function nh(n,t){var e=th(t)?t.asyncValidators:n;return Array.isArray(e)?Wf(e):e||null}function th(n){return null!=n&&!Array.isArray(n)&&"object"==typeof n}var eh=function(){function n(n,t){this.validator=n,this.asyncValidator=t,this._onCollectionChange=function(){},this.pristine=!0,this.touched=!1,this._onDisabledChange=[]}return Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"valid",{get:function(){return"VALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"invalid",{get:function(){return"INVALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"pending",{get:function(){return"PENDING"==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"disabled",{get:function(){return"DISABLED"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"enabled",{get:function(){return"DISABLED"!==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"dirty",{get:function(){return!this.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"untouched",{get:function(){return!this.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"updateOn",{get:function(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"},enumerable:!0,configurable:!0}),n.prototype.setValidators=function(n){this.validator=Xf(n)},n.prototype.setAsyncValidators=function(n){this.asyncValidator=nh(n)},n.prototype.clearValidators=function(){this.validator=null},n.prototype.clearAsyncValidators=function(){this.asyncValidator=null},n.prototype.markAsTouched=function(n){void 0===n&&(n={}),this.touched=!0,this._parent&&!n.onlySelf&&this._parent.markAsTouched(n)},n.prototype.markAsUntouched=function(n){void 0===n&&(n={}),this.touched=!1,this._pendingTouched=!1,this._forEachChild(function(n){n.markAsUntouched({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype.markAsDirty=function(n){void 0===n&&(n={}),this.pristine=!1,this._parent&&!n.onlySelf&&this._parent.markAsDirty(n)},n.prototype.markAsPristine=function(n){void 0===n&&(n={}),this.pristine=!0,this._pendingDirty=!1,this._forEachChild(function(n){n.markAsPristine({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype.markAsPending=function(n){void 0===n&&(n={}),this.status="PENDING",!1!==n.emitEvent&&this.statusChanges.emit(this.status),this._parent&&!n.onlySelf&&this._parent.markAsPending(n)},n.prototype.disable=function(n){void 0===n&&(n={}),this.status="DISABLED",this.errors=null,this._forEachChild(function(t){t.disable(i({},n,{onlySelf:!0}))}),this._updateValue(),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!0)})},n.prototype.enable=function(n){void 0===n&&(n={}),this.status="VALID",this._forEachChild(function(t){t.enable(i({},n,{onlySelf:!0}))}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!1)})},n.prototype._updateAncestors=function(n){this._parent&&!n.onlySelf&&(this._parent.updateValueAndValidity(n),this._parent._updatePristine(),this._parent._updateTouched())},n.prototype.setParent=function(n){this._parent=n},n.prototype.updateValueAndValidity=function(n){void 0===n&&(n={}),this._setInitialStatus(),this._updateValue(),this.enabled&&(this._cancelExistingSubscription(),this.errors=this._runValidator(),this.status=this._calculateStatus(),"VALID"!==this.status&&"PENDING"!==this.status||this._runAsyncValidator(n.emitEvent)),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._parent&&!n.onlySelf&&this._parent.updateValueAndValidity(n)},n.prototype._updateTreeValidity=function(n){void 0===n&&(n={emitEvent:!0}),this._forEachChild(function(t){return t._updateTreeValidity(n)}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})},n.prototype._setInitialStatus=function(){this.status=this._allControlsDisabled()?"DISABLED":"VALID"},n.prototype._runValidator=function(){return this.validator?this.validator(this):null},n.prototype._runAsyncValidator=function(n){var t=this;if(this.asyncValidator){this.status="PENDING";var e=Cf(this.asyncValidator(this));this._asyncValidationSubscription=e.subscribe(function(e){return t.setErrors(e,{emitEvent:n})})}},n.prototype._cancelExistingSubscription=function(){this._asyncValidationSubscription&&this._asyncValidationSubscription.unsubscribe()},n.prototype.setErrors=function(n,t){void 0===t&&(t={}),this.errors=n,this._updateControlsErrors(!1!==t.emitEvent)},n.prototype.get=function(n){return function(n,t,e){return null==t?null:(t instanceof Array||(t=t.split(".")),t instanceof Array&&0===t.length?null:t.reduce(function(n,t){return n instanceof oh?n.controls.hasOwnProperty(t)?n.controls[t]:null:n instanceof ih&&n.at(t)||null},n))}(this,n)},n.prototype.getError=function(n,t){var e=t?this.get(t):this;return e&&e.errors?e.errors[n]:null},n.prototype.hasError=function(n,t){return!!this.getError(n,t)},Object.defineProperty(n.prototype,"root",{get:function(){for(var n=this;n._parent;)n=n._parent;return n},enumerable:!0,configurable:!0}),n.prototype._updateControlsErrors=function(n){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),this._parent&&this._parent._updateControlsErrors(n)},n.prototype._initObservables=function(){this.valueChanges=new Ho,this.statusChanges=new Ho},n.prototype._calculateStatus=function(){return this._allControlsDisabled()?"DISABLED":this.errors?"INVALID":this._anyControlsHaveStatus("PENDING")?"PENDING":this._anyControlsHaveStatus("INVALID")?"INVALID":"VALID"},n.prototype._anyControlsHaveStatus=function(n){return this._anyControls(function(t){return t.status===n})},n.prototype._anyControlsDirty=function(){return this._anyControls(function(n){return n.dirty})},n.prototype._anyControlsTouched=function(){return this._anyControls(function(n){return n.touched})},n.prototype._updatePristine=function(n){void 0===n&&(n={}),this.pristine=!this._anyControlsDirty(),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype._updateTouched=function(n){void 0===n&&(n={}),this.touched=this._anyControlsTouched(),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype._isBoxedValue=function(n){return"object"==typeof n&&null!==n&&2===Object.keys(n).length&&"value"in n&&"disabled"in n},n.prototype._registerOnCollectionChange=function(n){this._onCollectionChange=n},n.prototype._setUpdateStrategy=function(n){th(n)&&null!=n.updateOn&&(this._updateOn=n.updateOn)},n}(),rh=function(n){function t(t,e,r){void 0===t&&(t=null);var o=n.call(this,Xf(e),nh(r,e))||this;return o._onChange=[],o._applyFormState(t),o._setUpdateStrategy(e),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o._initObservables(),o}return o(t,n),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this.value=this._pendingValue=n,this._onChange.length&&!1!==t.emitModelToViewChange&&this._onChange.forEach(function(n){return n(e.value,!1!==t.emitViewToModelChange)}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){void 0===t&&(t={}),this.setValue(n,t)},t.prototype.reset=function(n,t){void 0===n&&(n=null),void 0===t&&(t={}),this._applyFormState(n),this.markAsPristine(t),this.markAsUntouched(t),this.setValue(this.value,t),this._pendingChange=!1},t.prototype._updateValue=function(){},t.prototype._anyControls=function(n){return!1},t.prototype._allControlsDisabled=function(){return this.disabled},t.prototype.registerOnChange=function(n){this._onChange.push(n)},t.prototype._clearChangeFns=function(){this._onChange=[],this._onDisabledChange=[],this._onCollectionChange=function(){}},t.prototype.registerOnDisabledChange=function(n){this._onDisabledChange.push(n)},t.prototype._forEachChild=function(n){},t.prototype._syncPendingControls=function(){return!("submit"!==this.updateOn||(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),!this._pendingChange)||(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),0))},t.prototype._applyFormState=function(n){this._isBoxedValue(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n},t}(eh),oh=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.registerControl=function(n,t){return this.controls[n]?this.controls[n]:(this.controls[n]=t,t.setParent(this),t._registerOnCollectionChange(this._onCollectionChange),t)},t.prototype.addControl=function(n,t){this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.removeControl=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],t&&this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.contains=function(n){return this.controls.hasOwnProperty(n)&&this.controls[n].enabled},t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),Object.keys(n).forEach(function(r){e._throwIfControlMissing(r),e.controls[r].setValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),Object.keys(n).forEach(function(r){e.controls[r]&&e.controls[r].patchValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n={}),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this._reduceChildren({},function(n,t,e){return n[e]=t instanceof rh?t.value:t.getRawValue(),n})},t.prototype._syncPendingControls=function(){var n=this._reduceChildren(!1,function(n,t){return!!t._syncPendingControls()||n});return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!Object.keys(this.controls).length)throw new Error("\n There are no form controls registered with this group yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.controls[n])throw new Error("Cannot find form control with name: "+n+".")},t.prototype._forEachChild=function(n){var t=this;Object.keys(this.controls).forEach(function(e){return n(t.controls[e],e)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){t.setParent(n),t._registerOnCollectionChange(n._onCollectionChange)})},t.prototype._updateValue=function(){this.value=this._reduceValue()},t.prototype._anyControls=function(n){var t=this,e=!1;return this._forEachChild(function(r,o){e=e||t.contains(o)&&n(r)}),e},t.prototype._reduceValue=function(){var n=this;return this._reduceChildren({},function(t,e,r){return(e.enabled||n.disabled)&&(t[r]=e.value),t})},t.prototype._reduceChildren=function(n,t){var e=n;return this._forEachChild(function(n,r){e=t(e,n,r)}),e},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(Object.keys(this.controls)),r=e.next();!r.done;r=e.next())if(this.controls[r.value].enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return Object.keys(this.controls).length>0||this.disabled},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control with name: '"+e+"'.")})},t}(eh),ih=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.at=function(n){return this.controls[n]},t.prototype.push=function(n){this.controls.push(n),this._registerControl(n),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.insert=function(n,t){this.controls.splice(n,0,t),this._registerControl(t),this.updateValueAndValidity()},t.prototype.removeAt=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),this.updateValueAndValidity()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),t&&(this.controls.splice(n,0,t),this._registerControl(t)),this.updateValueAndValidity(),this._onCollectionChange()},Object.defineProperty(t.prototype,"length",{get:function(){return this.controls.length},enumerable:!0,configurable:!0}),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),n.forEach(function(n,r){e._throwIfControlMissing(r),e.at(r).setValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),n.forEach(function(n,r){e.at(r)&&e.at(r).patchValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n=[]),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this.controls.map(function(n){return n instanceof rh?n.value:n.getRawValue()})},t.prototype._syncPendingControls=function(){var n=this.controls.reduce(function(n,t){return!!t._syncPendingControls()||n},!1);return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!this.controls.length)throw new Error("\n There are no form controls registered with this array yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.at(n))throw new Error("Cannot find form control at index "+n)},t.prototype._forEachChild=function(n){this.controls.forEach(function(t,e){n(t,e)})},t.prototype._updateValue=function(){var n=this;this.value=this.controls.filter(function(t){return t.enabled||n.disabled}).map(function(n){return n.value})},t.prototype._anyControls=function(n){return this.controls.some(function(t){return t.enabled&&n(t)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){return n._registerControl(t)})},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control at index: "+e+".")})},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(this.controls),r=e.next();!r.done;r=e.next())if(r.value.enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return this.controls.length>0||this.disabled},t.prototype._registerControl=function(n){n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)},t}(eh),lh=Promise.resolve(null),uh=function(n){function t(t,e){var r=n.call(this)||this;return r.submitted=!1,r._directives=[],r.ngSubmit=new Ho,r.form=new oh({},Qf(t),Wf(e)),r}return o(t,n),t.prototype.ngAfterViewInit=function(){this._setUpdateStrategy()},Object.defineProperty(t.prototype,"formDirective",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"control",{get:function(){return this.form},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return[]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"controls",{get:function(){return this.form.controls},enumerable:!0,configurable:!0}),t.prototype.addControl=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);n.control=e.registerControl(n.name,n.control),Zf(n.control,n),n.control.updateValueAndValidity({emitEvent:!1}),t._directives.push(n)})},t.prototype.getControl=function(n){return this.form.get(n.path)},t.prototype.removeControl=function(n){var t=this;lh.then(function(){var e,r,o=t._findContainer(n.path);o&&o.removeControl(n.name),(r=(e=t._directives).indexOf(n))>-1&&e.splice(r,1)})},t.prototype.addFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path),r=new oh({});(function(n,t){null==n&&$f(t,"Cannot find control with"),n.validator=_f.compose([n.validator,t.validator]),n.asyncValidator=_f.composeAsync([n.asyncValidator,t.asyncValidator])})(r,n),e.registerControl(n.name,r),r.updateValueAndValidity({emitEvent:!1})})},t.prototype.removeFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);e&&e.removeControl(n.name)})},t.prototype.getFormGroup=function(n){return this.form.get(n.path)},t.prototype.updateModel=function(n,t){var e=this;lh.then(function(){e.form.get(n.path).setValue(t)})},t.prototype.setValue=function(n){this.control.setValue(n)},t.prototype.onSubmit=function(n){return this.submitted=!0,t=this._directives,this.form._syncPendingControls(),t.forEach(function(n){var t=n.control;"submit"===t.updateOn&&t._pendingChange&&(n.viewToModelUpdate(t._pendingValue),t._pendingChange=!1)}),this.ngSubmit.emit(n),!1;var t},t.prototype.onReset=function(){this.resetForm()},t.prototype.resetForm=function(n){void 0===n&&(n=void 0),this.form.reset(n),this.submitted=!1},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.form._updateOn=this.options.updateOn)},t.prototype._findContainer=function(n){return n.pop(),n.length?this.form.get(n):this.form},t}(yf),sh=function(){function n(){}return n.modelParentException=function(){throw new Error('\n ngModel cannot be used to register form controls with a parent formGroup directive. Try using\n formGroup\'s partner directive "formControlName" instead. Example:\n\n \n
\n \n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n firstName: new FormControl()\n });\n\n Or, if you\'d like to avoid registering this form control, indicate that it\'s standalone in ngModelOptions:\n\n Example:\n\n \n
\n \n \n
\n ')},n.formGroupNameException=function(){throw new Error("\n ngModel cannot be used to register form controls with a parent formGroupName or formArrayName directive.\n\n Option 1: Use formControlName instead of ngModel (reactive strategy):\n\n "+Hf+"\n\n Option 2: Update ngModel's parent be ngModelGroup (template-driven strategy):\n\n "+Rf)},n.missingNameException=function(){throw new Error('If ngModel is used within a form tag, either the name attribute must be set or the form\n control must be defined as \'standalone\' in ngModelOptions.\n\n Example 1: \n Example 2: ')},n.modelGroupParentException=function(){throw new Error("\n ngModelGroup cannot be used with a parent formGroup directive.\n\n Option 1: Use formGroupName instead of ngModelGroup (reactive strategy):\n\n "+Hf+"\n\n Option 2: Use a regular form tag instead of the formGroup directive (template-driven strategy):\n\n "+Rf)},n.ngFormWarning=function(){console.warn("\n It looks like you're using 'ngForm'.\n\n Support for using the 'ngForm' element selector has been deprecated in Angular v6 and will be removed\n in Angular v9.\n\n Use 'ng-form' instead.\n\n Before:\n \n\n After:\n \n ")},n}(),ah=new wn("NgFormSelectorWarning"),ch=function(n){function t(t,e,r){var o=n.call(this)||this;return o._parent=t,o._validators=e,o._asyncValidators=r,o}var e;return o(t,n),e=t,t.prototype._checkParentType=function(){this._parent instanceof e||this._parent instanceof uh||sh.modelGroupParentException()},t}(Jf),fh=Promise.resolve(null),hh=function(n){function t(t,e,r,o){var i=n.call(this)||this;return i.control=new rh,i._registered=!1,i.update=new Ho,i._parent=t,i._rawValidators=e||[],i._rawAsyncValidators=r||[],i.valueAccessor=function(n,t){if(!t)return null;Array.isArray(t)||$f(n,"Value accessor was not provided as an array for form control with");var e=void 0,r=void 0,o=void 0;return t.forEach(function(t){var i;t.constructor===Sf?e=t:(i=t,Kf.some(function(n){return i.constructor===n})?(r&&$f(n,"More than one built-in value accessor matches form control with"),r=t):(o&&$f(n,"More than one custom value accessor matches form control with"),o=t))}),o||r||e||($f(n,"No valid value accessor for form control with"),null)}(i,o),i}return o(t,n),t.prototype.ngOnChanges=function(n){this._checkForErrors(),this._registered||this._setUpControl(),"isDisabled"in n&&this._updateDisabled(n),function(n,t){if(!n.hasOwnProperty("model"))return!1;var e=n.model;return!!e.isFirstChange()||!Nn(t,e.currentValue)}(n,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeControl(this)},Object.defineProperty(t.prototype,"path",{get:function(){return this._parent?Gf(this.name,this._parent):[this.name]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._rawValidators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._rawAsyncValidators)},enumerable:!0,configurable:!0}),t.prototype.viewToModelUpdate=function(n){this.viewModel=n,this.update.emit(n)},t.prototype._setUpControl=function(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.control._updateOn=this.options.updateOn)},t.prototype._isStandalone=function(){return!this._parent||!(!this.options||!this.options.standalone)},t.prototype._setUpStandalone=function(){Zf(this.control,this),this.control.updateValueAndValidity({emitEvent:!1})},t.prototype._checkForErrors=function(){this._isStandalone()||this._checkParentType(),this._checkName()},t.prototype._checkParentType=function(){!(this._parent instanceof ch)&&this._parent instanceof Jf?sh.formGroupNameException():this._parent instanceof ch||this._parent instanceof uh||sh.modelParentException()},t.prototype._checkName=function(){this.options&&this.options.name&&(this.name=this.options.name),this._isStandalone()||this.name||sh.missingNameException()},t.prototype._updateValue=function(n){var t=this;fh.then(function(){t.control.setValue(n,{emitViewToModelChange:!1})})},t.prototype._updateDisabled=function(n){var t=this,e=n.isDisabled.currentValue,r=""===e||e&&"false"!==e;fh.then(function(){r&&!t.control.disabled?t.control.disable():!r&&t.control.disabled&&t.control.enable()})},t}(Nf),ph=function(){return function(){}}(),dh=function(){function n(){}var t;return t=n,n.withConfig=function(n){return{ngModule:t,providers:[{provide:ah,useValue:n.warnOnDeprecatedNgFormSelector}]}},n}(),gh=Pl({encapsulation:2,styles:[],data:{}});function vh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function yh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","20"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["20"]))],function(n,t){n(t,1,0,"20"),n(t,2,0,"20")},null)}function mh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","50"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["50"]))],function(n,t){n(t,1,0,"50"),n(t,2,0,"50")},null)}function bh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","100"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["100"]))],function(n,t){n(t,1,0,"100"),n(t,2,0,"100")},null)}function _h(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){var e=t.component;n(t,1,0,e.totalNumberOfRiskHotspots),n(t,2,0,e.totalNumberOfRiskHotspots)},function(n,t){n(t,3,0,t.component.translations.all)})}function wh(n){return bs(0,[(n()(),lu(0,0,null,null,17,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,1).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,1).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.numberOfRiskHotspots=e)&&r),r},null,null)),Qu(1,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(3,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(5,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(6,0,null,null,3,"option",[["value","10"]],null,null,null,null,null)),Qu(7,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(8,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["10"])),(n()(),iu(16777216,null,null,1,null,yh)),Qu(11,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mh)),Qu(13,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bh)),Qu(15,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,_h)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,3,0,e.settings.numberOfRiskHotspots),n(t,7,0,"10"),n(t,8,0,"10"),n(t,11,0,e.totalNumberOfRiskHotspots>10),n(t,13,0,e.totalNumberOfRiskHotspots>20),n(t,15,0,e.totalNumberOfRiskHotspots>50),n(t,17,0,e.totalNumberOfRiskHotspots>100)},function(n,t){n(t,0,0,Vu(t,5).ngClassUntouched,Vu(t,5).ngClassTouched,Vu(t,5).ngClassPristine,Vu(t,5).ngClassDirty,Vu(t,5).ngClassValid,Vu(t,5).ngClassInvalid,Vu(t,5).ngClassPending)})}function Ch(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null))],null,null)}function Eh(n){return bs(0,[(n()(),lu(0,0,null,null,7,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting(""+n.context.index,e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),lu(7,0,null,null,0,"i",[["class","icon-info-circled"]],null,null,null,null,null))],function(n,t){var e=t.component,r=n(t,4,0,e.settings.sortBy===""+t.context.index&&"desc"===e.settings.sortOrder,e.settings.sortBy===""+t.context.index&&"asc"===e.settings.sortOrder,e.settings.sortBy!==""+t.context.index);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.context.$implicit.name),n(t,6,0,ru(1,"",t.context.$implicit.explanationUrl,""))})}function xh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"td",[["class","right"]],null,null,null,null,null)),Qu(1,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(2,{lightred:0,lightgreen:1}),(n()(),vs(3,null,["",""]))],function(n,t){var e=n(t,2,0,t.context.$implicit.exceeded,!t.context.$implicit.exceeded);n(t,1,0,"right",e)},function(n,t){n(t,3,0,t.context.$implicit.value)})}function Oh(n){return bs(0,[(n()(),lu(0,0,null,null,10,"tr",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"td",[],null,null,null,null,null)),(n()(),vs(2,null,["",""])),(n()(),lu(3,0,null,null,2,"td",[],null,null,null,null,null)),(n()(),lu(4,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,2,"td",[],[[8,"title",0]],null,null,null,null)),(n()(),lu(7,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(8,null,[" "," "])),(n()(),iu(16777216,null,null,1,null,xh)),Qu(10,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){n(t,10,0,t.context.$implicit.metrics)},function(n,t){n(t,2,0,t.context.$implicit.assembly),n(t,4,0,t.context.$implicit.reportPath),n(t,5,0,t.context.$implicit.class),n(t,6,0,t.context.$implicit.methodName),n(t,7,0,t.context.$implicit.reportPath+"#file"+t.context.$implicit.fileIndex+"_line"+t.context.$implicit.line),n(t,8,0,t.context.$implicit.methodShortName)})}function kh(n){return bs(0,[(n()(),lu(0,0,null,null,62,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,28,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,12,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,11,"select",[["name","assembly"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.assembly=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{name:[0,"name"],model:[1,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,vh)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(15,0,null,null,4,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(16,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(17,null,["",""])),(n()(),iu(16777216,null,null,1,null,wh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,0,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(21,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(22,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(23,null,[""," "])),(n()(),lu(24,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,25)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,25).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,25)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,25)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(25,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(27,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(29,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(30,0,null,null,32,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(31,0,null,null,5,"colgroup",[],null,null,null,null,null)),(n()(),lu(32,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(33,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(34,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ch)),Qu(36,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(37,0,null,null,21,"thead",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,20,"tr",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(40,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("assembly",e)&&r),r},null,null)),(n()(),lu(41,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(42,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(43,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(44,null,["",""])),(n()(),lu(45,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(46,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("class",e)&&r),r},null,null)),(n()(),lu(47,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(48,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(49,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(50,null,["",""])),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("method",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),iu(16777216,null,null,1,null,Eh)),Qu(58,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(59,0,null,null,3,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,2,null,Oh)),Qu(61,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(t=0,e=ec,r=[],Ku(-1,t|=16,null,0,e,e,r))],function(n,t){var e=t.component;n(t,6,0,"assembly",e.settings.assembly),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.assemblies),n(t,19,0,e.totalNumberOfRiskHotspots>10),n(t,27,0,e.settings.filter),n(t,36,0,e.riskHotspotMetrics);var r=n(t,43,0,"assembly"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"assembly"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"assembly"!==e.settings.sortBy);n(t,42,0,"icon-down-dir",r);var o=n(t,49,0,"class"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"class"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"class"!==e.settings.sortBy);n(t,48,0,"icon-down-dir",o);var i=n(t,55,0,"method"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"method"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"method"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",i),n(t,58,0,e.riskHotspotMetrics),n(t,61,0,function(n,t,e,r){if(_t.isWrapped(r)){r=_t.unwrap(r);var o=n.def.nodes[61].bindingIndex+0,i=_t.unwrap(n.oldValues[o]);n.oldValues[o]=new _t(i)}return r}(t,0,0,Vu(t,62).transform(e.riskHotspots,0,e.settings.numberOfRiskHotspots)))},function(n,t){var e=t.component;n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.assembly),n(t,17,0,e.translations.top),n(t,23,0,e.translations.filter),n(t,24,0,Vu(t,29).ngClassUntouched,Vu(t,29).ngClassTouched,Vu(t,29).ngClassPristine,Vu(t,29).ngClassDirty,Vu(t,29).ngClassValid,Vu(t,29).ngClassInvalid,Vu(t,29).ngClassPending),n(t,44,0,e.translations.assembly),n(t,50,0,e.translations.class),n(t,56,0,e.translations.method)});var t,e,r}function Sh(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,kh)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.totalNumberOfRiskHotspots>0)},null)}function Ah(n){return bs(0,[(n()(),lu(0,0,null,null,1,"risk-hotspots",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Sh,gh)),Qu(1,114688,null,0,Na,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Th=xu("risk-hotspots",Na,Ah,{},{},[]),Ih=function(){function n(){this.grayVisible=!0,this.greenVisible=!1,this.redVisible=!1,this.greenClass="",this.redClass="",this._percentage=NaN}return Object.defineProperty(n.prototype,"percentage",{get:function(){return this._percentage},set:function(n){this._percentage=n,this.grayVisible=isNaN(n),this.greenVisible=!isNaN(n)&&Math.round(n)>0,this.redVisible=!isNaN(n)&&100-Math.round(n)>0,this.greenClass="covered"+Math.round(n),this.redClass="covered"+(100-Math.round(n))},enumerable:!0,configurable:!0}),n}(),Ph=Pl({encapsulation:2,styles:[],data:{}});function Nh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[["class","gray covered100"]],null,null,null,null,null))],null,null)}function Dh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"green ",t.component.greenClass,""))})}function Mh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"red ",t.component.redClass,""))})}function Vh(n){return bs(2,[(n()(),lu(0,0,null,null,6,"table",[["class","coverage"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Nh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Dh)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Mh)),Qu(6,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,e.grayVisible),n(t,4,0,e.greenVisible),n(t,6,0,e.redVisible)},null)}var Hh=function(){return function(){this.element=null,this.collapsed=!1,this.branchCoverageAvailable=!1}}(),Rh=Pl({encapsulation:2,styles:[],data:{}});function jh(n){return bs(0,[(n()(),lu(0,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.element.branchCoveragePercentage)})}function Lh(n){return bs(0,[(n()(),lu(0,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.element.branchCoverage)},null)}function zh(n){return bs(2,[(n()(),lu(0,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.element.toggleCollapse(e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{ngClass:[0,"ngClass"]},null),gs(4,{"icon-plus":0,"icon-minus":1}),(n()(),vs(5,null,[" ",""])),(n()(),lu(6,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(9,null,["",""])),(n()(),lu(10,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(11,null,["",""])),(n()(),lu(12,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(13,null,["",""])),(n()(),lu(14,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(15,null,["",""])),(n()(),lu(16,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(17,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(18,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,jh)),Qu(20,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Lh)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component,r=n(t,4,0,e.element.collapsed,!e.element.collapsed);n(t,3,0,r),n(t,18,0,e.element.coverage),n(t,20,0,e.branchCoverageAvailable),n(t,22,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,5,0,e.element.name),n(t,7,0,e.element.coveredLines),n(t,9,0,e.element.uncoveredLines),n(t,11,0,e.element.coverableLines),n(t,13,0,e.element.totalLines),n(t,15,0,e.element.coveragePercentage)})}var Fh=function(){function n(){this.path=null,this._historicCoverages=[]}return Object.defineProperty(n.prototype,"historicCoverages",{get:function(){return this._historicCoverages},set:function(n){if(this._historicCoverages=n,n.length>1){for(var t="",e=0;et?"lightgreen":n1),n(t,4,0,null!==e.clazz.currentHistoricCoverage),n(t,6,0,null===e.clazz.currentHistoricCoverage)},null)}function ap(n){return bs(0,[(n()(),lu(0,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.clazz.branchCoverage)},null)}function cp(n){return bs(2,[(n()(),lu(0,0,null,null,4,"td",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,qh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,$h)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(5,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Qh)),Qu(7,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Wh)),Qu(9,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(10,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Kh)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Jh)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Yh)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Xh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,np)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,tp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(25,0,null,null,6,"td",[["class","right"]],[[8,"title",0]],null,null,null,null)),(n()(),iu(16777216,null,null,1,null,ep)),Qu(27,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,rp)),Qu(29,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,op)),Qu(31,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(32,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(33,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(34,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,sp)),Qu(36,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,ap)),Qu(38,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,""!==e.clazz.reportPath),n(t,4,0,""===e.clazz.reportPath),n(t,7,0,null!==e.clazz.currentHistoricCoverage),n(t,9,0,null===e.clazz.currentHistoricCoverage),n(t,12,0,null!==e.clazz.currentHistoricCoverage),n(t,14,0,null===e.clazz.currentHistoricCoverage),n(t,17,0,null!==e.clazz.currentHistoricCoverage),n(t,19,0,null===e.clazz.currentHistoricCoverage),n(t,22,0,null!==e.clazz.currentHistoricCoverage),n(t,24,0,null===e.clazz.currentHistoricCoverage),n(t,27,0,e.clazz.lineCoverageHistory.length>1),n(t,29,0,null!==e.clazz.currentHistoricCoverage),n(t,31,0,null===e.clazz.currentHistoricCoverage),n(t,34,0,e.clazz.coverage),n(t,36,0,e.branchCoverageAvailable),n(t,38,0,e.branchCoverageAvailable)},function(n,t){n(t,25,0,t.component.clazz.coverageType)})}var fp=Pl({encapsulation:2,styles:[],data:{}});function hp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.noGrouping)})}function pp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.byAssembly)})}function dp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){var e=t.component;n(t,1,0,e.translations.byNamespace+" "+e.settings.grouping)})}function gp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function vp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"br",[],null,null,null,null,null))],null,null)}function yp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageIncreaseOnly"),n(t,2,0,"branchCoverageIncreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageIncreaseOnly)})}function mp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageDecreaseOnly"),n(t,2,0,"branchCoverageDecreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageDecreaseOnly)})}function bp(n){return bs(0,[(n()(),lu(0,0,null,null,26,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,25,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,2).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,2).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionType=e)&&r),r},null,null)),Qu(2,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(4,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(6,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(7,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(8,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(9,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(10,null,["",""])),(n()(),lu(11,0,null,null,3,"option",[["value","allChanges"]],null,null,null,null,null)),Qu(12,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(13,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(14,null,["",""])),(n()(),lu(15,0,null,null,3,"option",[["value","lineCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(16,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(17,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(18,null,["",""])),(n()(),lu(19,0,null,null,3,"option",[["value","lineCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(20,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(21,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(22,null,["",""])),(n()(),iu(16777216,null,null,1,null,yp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mp)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,4,0,e.settings.historyComparisionType),n(t,8,0,""),n(t,9,0,""),n(t,12,0,"allChanges"),n(t,13,0,"allChanges"),n(t,16,0,"lineCoverageIncreaseOnly"),n(t,17,0,"lineCoverageIncreaseOnly"),n(t,20,0,"lineCoverageDecreaseOnly"),n(t,21,0,"lineCoverageDecreaseOnly"),n(t,24,0,e.branchCoverageAvailable),n(t,26,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,1,0,Vu(t,6).ngClassUntouched,Vu(t,6).ngClassTouched,Vu(t,6).ngClassPristine,Vu(t,6).ngClassDirty,Vu(t,6).ngClassValid,Vu(t,6).ngClassInvalid,Vu(t,6).ngClassPending),n(t,10,0,e.translations.filter),n(t,14,0,e.translations.allChanges),n(t,18,0,e.translations.lineCoverageIncreaseOnly),n(t,22,0,e.translations.lineCoverageDecreaseOnly)})}function _p(n){return bs(0,[(n()(),lu(0,0,null,null,18,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,13,"div",[],null,null,null,null,null)),(n()(),vs(2,null,[" "," "])),(n()(),lu(3,0,null,null,11,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionDate=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCurrentHistoricCoverage()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,gp)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,vp)),Qu(16,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bp)),Qu(18,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component;n(t,6,0,e.settings.historyComparisionDate),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.historicCoverageExecutionTimes),n(t,16,0,""!==e.settings.historyComparisionDate),n(t,18,0,""!==e.settings.historyComparisionDate)},function(n,t){var e=t.component;n(t,2,0,e.translations.compareHistory),n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.date)})}function wp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null))],null,null)}function Cp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null))],null,null)}function Ep(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"coverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.coverage)})}function xp(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("branchcoverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"branchcoverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"branchcoverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"branchcoverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.branchCoverage)})}function Op(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["codeelement-row",""]],null,null,null,zh,Rh)),Qu(1,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null)],function(n,t){n(t,1,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable)},null)}function kp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Sp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,kp)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ap(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class","namespace"],["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Tp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ap)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ip(n){return bs(0,[(n()(),lu(0,0,null,null,4,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"tr",[["class","namespace"],["codeelement-row",""]],null,null,null,zh,Rh)),Qu(2,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null),(n()(),iu(16777216,null,null,1,null,Tp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){n(t,2,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable),n(t,4,0,t.parent.context.$implicit.classes)},null)}function Pp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ip)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Np(n){return bs(0,[(n()(),lu(0,0,null,null,6,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Op)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Sp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,Pp)),Qu(6,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r),n(t,4,0,t.context.$implicit.classes),n(t,6,0,t.context.$implicit.subElements)},null)}function Dp(n){return bs(0,[(n()(),lu(0,0,null,null,87,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,34,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,5,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.collapseAll(e)&&r),r},null,null)),(n()(),vs(4,null,["",""])),(n()(),vs(-1,null,[" | "])),(n()(),lu(6,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.expandAll(e)&&r),r},null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,15,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,hp)),Qu(10,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,pp)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,dp)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,0,"br",[],null,null,null,null,null)),(n()(),vs(16,null,[" "," "])),(n()(),lu(17,0,null,null,6,"input",[["min","-1"],["step","1"],["type","range"]],[[8,"max",0],[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"],[null,"change"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,18)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,18).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,18)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,18)._compositionEnd(e.target.value)&&r),"change"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"input"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,19).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.grouping=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCoverageInfo()&&r),r},null,null)),Qu(18,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Qu(19,16384,null,0,Vf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n,t){return[n,t]},[Sf,Vf]),Qu(21,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(23,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(24,0,null,null,2,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,_p)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(27,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(28,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(29,null,[""," "])),(n()(),lu(30,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,31)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,31).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,31)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,31)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),r},null,null)),Qu(31,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(33,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(35,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(36,0,null,null,51,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(37,0,null,null,11,"colgroup",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,0,"col",[["class","column90"]],null,null,null,null,null)),(n()(),lu(40,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null)),(n()(),lu(41,0,null,null,0,"col",[["class","column100"]],null,null,null,null,null)),(n()(),lu(42,0,null,null,0,"col",[["class","column70"]],null,null,null,null,null)),(n()(),lu(43,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null)),(n()(),lu(44,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,wp)),Qu(46,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Cp)),Qu(48,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(49,0,null,null,35,"thead",[],null,null,null,null,null)),(n()(),lu(50,0,null,null,34,"tr",[],null,null,null,null,null)),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("name",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),lu(57,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(58,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("covered",e)&&r),r},null,null)),(n()(),lu(59,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(60,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(61,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(62,null,["",""])),(n()(),lu(63,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(64,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("uncovered",e)&&r),r},null,null)),(n()(),lu(65,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(66,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(67,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(68,null,["",""])),(n()(),lu(69,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(70,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverable",e)&&r),r},null,null)),(n()(),lu(71,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(72,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(73,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(74,null,["",""])),(n()(),lu(75,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(76,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("total",e)&&r),r},null,null)),(n()(),lu(77,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(78,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(79,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(80,null,["",""])),(n()(),iu(16777216,null,null,1,null,Ep)),Qu(82,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,xp)),Qu(84,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(85,0,null,null,2,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Np)),Qu(87,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){var e=t.component;n(t,10,0,-1===e.settings.grouping),n(t,12,0,0===e.settings.grouping),n(t,14,0,e.settings.grouping>0),n(t,21,0,e.settings.grouping),n(t,26,0,e.historicCoverageExecutionTimes.length>0),n(t,33,0,e.settings.filter),n(t,46,0,e.branchCoverageAvailable),n(t,48,0,e.branchCoverageAvailable);var r=n(t,55,0,"name"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"name"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"name"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",r);var o=n(t,61,0,"covered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"covered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"covered"!==e.settings.sortBy);n(t,60,0,"icon-down-dir",o);var i=n(t,67,0,"uncovered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"uncovered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"uncovered"!==e.settings.sortBy);n(t,66,0,"icon-down-dir",i);var l=n(t,73,0,"coverable"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverable"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverable"!==e.settings.sortBy);n(t,72,0,"icon-down-dir",l);var u=n(t,79,0,"total"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"total"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"total"!==e.settings.sortBy);n(t,78,0,"icon-down-dir",u),n(t,82,0,e.branchCoverageAvailable),n(t,84,0,e.branchCoverageAvailable),n(t,87,0,e.codeElements)},function(n,t){var e=t.component;n(t,4,0,e.translations.collapseAll),n(t,7,0,e.translations.expandAll),n(t,16,0,e.translations.grouping),n(t,17,0,e.settings.groupingMaximum,Vu(t,23).ngClassUntouched,Vu(t,23).ngClassTouched,Vu(t,23).ngClassPristine,Vu(t,23).ngClassDirty,Vu(t,23).ngClassValid,Vu(t,23).ngClassInvalid,Vu(t,23).ngClassPending),n(t,29,0,e.translations.filter),n(t,30,0,Vu(t,35).ngClassUntouched,Vu(t,35).ngClassTouched,Vu(t,35).ngClassPristine,Vu(t,35).ngClassDirty,Vu(t,35).ngClassValid,Vu(t,35).ngClassInvalid,Vu(t,35).ngClassPending),n(t,56,0,e.translations.name),n(t,62,0,e.translations.covered),n(t,68,0,e.translations.uncovered),n(t,74,0,e.translations.coverable),n(t,80,0,e.translations.total)})}function Mp(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,Dp)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.codeElements.length>0)},null)}function Vp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"coverage-info",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Mp,fp)),Qu(1,114688,null,0,ja,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Hp=xu("coverage-info",ja,Vp,{},{},[]),Rp=ka(Aa,[Na,ja],function(n){return function(n){for(var t={},e=[],r=!1,o=0;odiv { width: 25%; display: inline-block; } -.customizebox div.right input { font-size: 0.8em; width: 150px; } -#namespaceslider { width: 200px; display: inline-block; margin-left: 8px; } - -.percentagebarundefined { - border-left: 2px solid #fff; - padding-left: 3px; -} -.percentagebar0 { - border-left: 2px solid #c10909; - padding-left: 3px; -} -.percentagebar10 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 90%, #0aad0a 90%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar20 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 80%, #0aad0a 80%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar30 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 70%, #0aad0a 70%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar40 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 60%, #0aad0a 60%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar50 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 50%, #0aad0a 50%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar60 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 40%, #0aad0a 40%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar70 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 30%, #0aad0a 30%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar80 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 20%, #0aad0a 20%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar90 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 10%, #0aad0a 10%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar100 { - border-left: 2px solid #0aad0a; - padding-left: 3px; -} - -.hidden, .ng-hide { display: none; } -.right { text-align: right; } -.center { text-align: center; } -.rightmargin { padding-right: 8px; } -.leftmargin { padding-left: 5px; } -.green { background-color: #0aad0a; } -.lightgreen { background-color: #dcf4dc; } -.red { background-color: #c10909; } -.lightred { background-color: #f7dede; } -.orange { background-color: #FFA500; } -.lightorange { background-color: #FFEFD5; } -.gray { background-color: #dcdcdc; } -.lightgray { color: #888888; } -.lightgraybg { background-color: #dadada; } - -.toggleZoom { text-align:right; } - -.ct-chart { position: relative; } -.ct-chart .ct-line { stroke-width: 2px !important; } -.ct-chart .ct-point { stroke-width: 6px !important; transition: stroke-width .2s; } -.ct-chart .ct-point:hover { stroke-width: 10px !important; } -.ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { stroke: #c00 !important;} -.ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { stroke: #1c2298 !important;} - -.tinylinecoveragechart, .tinybranchcoveragechart { background-color: #fff; margin-left: -3px; float: left; border: solid 1px #c1c1c1; width: 30px; height: 18px; } -.historiccoverageoffset { margin-top: 7px; } - -.tinylinecoveragechart .ct-line, .tinybranchcoveragechart .ct-line { stroke-width: 1px !important; } -.tinybranchcoveragechart .ct-series.ct-series-a .ct-line { stroke: #1c2298 !important; } - -.linecoverage { background-color: #c00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } -.branchcoverage { background-color: #1c2298; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } - -.tooltip { position: absolute; display: none; padding: 5px; background: #F4C63D;color: #453D3F; pointer-events: none; z-index: 1; } -.tooltip:after { content: ""; position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin-left: -15px; border: 15px solid transparent; border-top-color: #F4C63D; } - -.column1324 { max-width: 1324px; } -.column674 { max-width: 674px; } -.column60 { width: 60px; } -.column70 { width: 70px; } -.column90 { width: 90px; } -.column98 { width: 98px; } -.column100 { width: 100px; } -.column105 { width: 105px; } -.column112 { width: 112px; } -.column135 { width: 135px; } -.column150 { width: 150px; } - -.covered0 { width: 0px; } -.covered1 { width: 1px; } -.covered2 { width: 2px; } -.covered3 { width: 3px; } -.covered4 { width: 4px; } -.covered5 { width: 5px; } -.covered6 { width: 6px; } -.covered7 { width: 7px; } -.covered8 { width: 8px; } -.covered9 { width: 9px; } -.covered10 { width: 10px; } -.covered11 { width: 11px; } -.covered12 { width: 12px; } -.covered13 { width: 13px; } -.covered14 { width: 14px; } -.covered15 { width: 15px; } -.covered16 { width: 16px; } -.covered17 { width: 17px; } -.covered18 { width: 18px; } -.covered19 { width: 19px; } -.covered20 { width: 20px; } -.covered21 { width: 21px; } -.covered22 { width: 22px; } -.covered23 { width: 23px; } -.covered24 { width: 24px; } -.covered25 { width: 25px; } -.covered26 { width: 26px; } -.covered27 { width: 27px; } -.covered28 { width: 28px; } -.covered29 { width: 29px; } -.covered30 { width: 30px; } -.covered31 { width: 31px; } -.covered32 { width: 32px; } -.covered33 { width: 33px; } -.covered34 { width: 34px; } -.covered35 { width: 35px; } -.covered36 { width: 36px; } -.covered37 { width: 37px; } -.covered38 { width: 38px; } -.covered39 { width: 39px; } -.covered40 { width: 40px; } -.covered41 { width: 41px; } -.covered42 { width: 42px; } -.covered43 { width: 43px; } -.covered44 { width: 44px; } -.covered45 { width: 45px; } -.covered46 { width: 46px; } -.covered47 { width: 47px; } -.covered48 { width: 48px; } -.covered49 { width: 49px; } -.covered50 { width: 50px; } -.covered51 { width: 51px; } -.covered52 { width: 52px; } -.covered53 { width: 53px; } -.covered54 { width: 54px; } -.covered55 { width: 55px; } -.covered56 { width: 56px; } -.covered57 { width: 57px; } -.covered58 { width: 58px; } -.covered59 { width: 59px; } -.covered60 { width: 60px; } -.covered61 { width: 61px; } -.covered62 { width: 62px; } -.covered63 { width: 63px; } -.covered64 { width: 64px; } -.covered65 { width: 65px; } -.covered66 { width: 66px; } -.covered67 { width: 67px; } -.covered68 { width: 68px; } -.covered69 { width: 69px; } -.covered70 { width: 70px; } -.covered71 { width: 71px; } -.covered72 { width: 72px; } -.covered73 { width: 73px; } -.covered74 { width: 74px; } -.covered75 { width: 75px; } -.covered76 { width: 76px; } -.covered77 { width: 77px; } -.covered78 { width: 78px; } -.covered79 { width: 79px; } -.covered80 { width: 80px; } -.covered81 { width: 81px; } -.covered82 { width: 82px; } -.covered83 { width: 83px; } -.covered84 { width: 84px; } -.covered85 { width: 85px; } -.covered86 { width: 86px; } -.covered87 { width: 87px; } -.covered88 { width: 88px; } -.covered89 { width: 89px; } -.covered90 { width: 90px; } -.covered91 { width: 91px; } -.covered92 { width: 92px; } -.covered93 { width: 93px; } -.covered94 { width: 94px; } -.covered95 { width: 95px; } -.covered96 { width: 96px; } -.covered97 { width: 97px; } -.covered98 { width: 98px; } -.covered99 { width: 99px; } -.covered100 { width: 100px; } - - @media print { - html, body { background-color: #fff; } - .container { max-width: 100%; width: 100%; padding: 0; } - .overview colgroup col:first-child { width: 300px; } -} - -.icon-up-dir_active { - background-image: url(icon_up-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDEyMTZxMCAyNi0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMC00NS0xOXQtMTktNDUgMTktNDVsNDQ4LTQ0OHExOS0xOSA0NS0xOXQ0NSAxOWw0NDggNDQ4cTE5IDE5IDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-down-dir_active { - background-image: url(icon_up-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-down-dir { - background-image: url(icon_down-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-info-circled { - background-image: url(icon_info-circled.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; -} -.icon-plus { - background-image: url(icon_plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTQxNnY0MTZxMCA0MC0yOCA2OHQtNjggMjhoLTE5MnEtNDAgMC02OC0yOHQtMjgtNjh2LTQxNmgtNDE2cS00MCAwLTY4LTI4dC0yOC02OHYtMTkycTAtNDAgMjgtNjh0NjgtMjhoNDE2di00MTZxMC00MCAyOC02OHQ2OC0yOGgxOTJxNDAgMCA2OCAyOHQyOCA2OHY0MTZoNDE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-minus { - background-image: url(icon_minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTEyMTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGgxMjE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-wrench { - background-image: url(icon_wrench.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTQ0OCAxNDcycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em02NDQtNDIwbC02ODIgNjgycS0zNyAzNy05MCAzNy01MiAwLTkxLTM3bC0xMDYtMTA4cS0zOC0zNi0zOC05MCAwLTUzIDM4LTkxbDY4MS02ODFxMzkgOTggMTE0LjUgMTczLjV0MTczLjUgMTE0LjV6bTYzNC00MzVxMCAzOS0yMyAxMDYtNDcgMTM0LTE2NC41IDIxNy41dC0yNTguNSA4My41cS0xODUgMC0zMTYuNS0xMzEuNXQtMTMxLjUtMzE2LjUgMTMxLjUtMzE2LjUgMzE2LjUtMTMxLjVxNTggMCAxMjEuNSAxNi41dDEwNy41IDQ2LjVxMTYgMTEgMTYgMjh0LTE2IDI4bC0yOTMgMTY5djIyNGwxOTMgMTA3cTUtMyA3OS00OC41dDEzNS41LTgxIDcwLjUtMzUuNXExNSAwIDIzLjUgMTB0OC41IDI1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-fork { - background-image: url(icon_fork.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNmZmYiIC8+PHBhdGggZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-cube { - background-image: url(icon_cube.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTg5NiAxNjI5bDY0MC0zNDl2LTYzNmwtNjQwIDIzM3Y3NTJ6bS02NC04NjVsNjk4LTI1NC02OTgtMjU0LTY5OCAyNTR6bTgzMi0yNTJ2NzY4cTAgMzUtMTggNjV0LTQ5IDQ3bC03MDQgMzg0cS0yOCAxNi02MSAxNnQtNjEtMTZsLTcwNC0zODRxLTMxLTE3LTQ5LTQ3dC0xOC02NXYtNzY4cTAtNDAgMjMtNzN0NjEtNDdsNzA0LTI1NnEyMi04IDQ0LTh0NDQgOGw3MDQgMjU2cTM4IDE0IDYxIDQ3dDIzIDczeiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-search-plus { - background-image: url(icon_search-plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtMjI0djIyNHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di0yMjRoLTIyNHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTY0cTAtMTMgOS41LTIyLjV0MjIuNS05LjVoMjI0di0yMjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41djIyNGgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-search-minus { - background-image: url(icon_search-minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNTc2cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg1NzZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} - -.ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} \ No newline at end of file From f6c0619b074340b366f0d0fb6208ba3c0ce2aac0 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 14:22:36 +0800 Subject: [PATCH 041/267] delete --- .../2021-03-26_16-34-47_CoverageHistory.xml | 12 - .../report/ClassLibrary1_Calculation.htm | 82 -- .../Lab.OpenCoverDemo/report/Cobertura.xml | 105 -- .../report/UnitTestProject1_UnitTest1.htm | 88 -- .../report/UnitTestProject2_UnitTest1.htm | 75 - .../report/badge_linecoverage.png | Bin 2925 -> 0 bytes .../report/badge_linecoverage.svg | 88 -- .../Lab.OpenCoverDemo/report/class.js | 207 --- .../Lab.OpenCoverDemo/report/coverage.xml | 1270 ----------------- .../Lab.OpenCoverDemo/report/icon_cube.svg | 2 - .../report/icon_down-dir_active.svg | 2 - .../Lab.OpenCoverDemo/report/icon_fork.svg | 2 - .../report/icon_info-circled.svg | 2 - .../Lab.OpenCoverDemo/report/icon_minus.svg | 2 - .../Lab.OpenCoverDemo/report/icon_plus.svg | 2 - .../report/icon_search-minus.svg | 2 - .../report/icon_search-plus.svg | 2 - .../Lab.OpenCoverDemo/report/icon_up-dir.svg | 2 - .../report/icon_up-dir_active.svg | 2 - .../Lab.OpenCoverDemo/report/icon_wrench.svg | 2 - .../Lab.OpenCoverDemo/report/index.htm | 71 - .../Lab.OpenCoverDemo/report/main.js | 274 ---- .../Lab.OpenCoverDemo/report/report.css | 357 ----- 23 files changed, 2651 deletions(-) delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js delete mode 100644 CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/report.css diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml deleted file mode 100644 index 7531ba24..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/history/2021-03-26_16-34-47_CoverageHistory.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm deleted file mode 100644 index 6976b753..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/ClassLibrary1_Calculation.htm +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - -ClassLibrary1.Calculation - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:ClassLibrary1.Calculation
Assembly:ClassLibrary1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs
Covered lines:3
Uncovered lines:6
Coverable lines:9
Total lines:18
Line coverage:33.3% (3 of 9)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
Add(...)10100%100%1
Sub(...)100%0%2
Sub1(...)100%0%2
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\ClassLibrary1\Calculation.cs

- - - - - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1namespace ClassLibrary1
 2{
 3    public class Calculation
 4    {
 5        public int Add(int firstNumber, int secondNumber)
 26        {
 27            return firstNumber + secondNumber;
 28        }
 9        public int Sub(int firstNumber, int secondNumber)
 010        {
 011            return firstNumber + secondNumber;
 012        }
 13        public int Sub1(int firstNumber, int secondNumber)
 014        {
 015            return firstNumber + secondNumber;
 016        }
 17    }
 18}
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml deleted file mode 100644 index 105f9442..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/Cobertura.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm deleted file mode 100644 index 1ec7556c..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject1_UnitTest1.htm +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - -UnitTestProject1.UnitTest1 - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:UnitTestProject1.UnitTest1
Assembly:UnitTestProject1
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs
Covered lines:9
Uncovered lines:1
Coverable lines:10
Total lines:26
Line coverage:90% (9 of 10)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
TestMethod2()1080%100%1.01
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\UnitTest1.cs

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1using System;
 2using ClassLibrary1;
 3using Microsoft.VisualStudio.TestTools.UnitTesting;
 4
 5namespace UnitTestProject1
 6{
 7    [TestClass]
 8    public class UnitTest1
 9    {
 10        [TestMethod]
 11        public void TestMethod1()
 112        {
 113            var calculation = new Calculation();
 114            var actual = calculation.Add(1, 1);
 115            Assert.AreEqual(2,actual);
 116        }
 17
 18        [TestMethod]
 19        public void TestMethod2()
 120        {
 121            var calculation = new Calculation();
 122            var actual = calculation.Add(1, 1);
 123            Assert.AreEqual(1,actual);
 024        }
 25    }
 26}
-
-
-
-

Methods/Properties

-TestMethod1()
-TestMethod2()
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm deleted file mode 100644 index 7d13ebe2..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/UnitTestProject2_UnitTest1.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - -UnitTestProject2.UnitTest1 - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - -
Class:UnitTestProject2.UnitTest1
Assembly:UnitTestProject2
File(s):E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs
Covered lines:3
Uncovered lines:0
Coverable lines:3
Total lines:15
Line coverage:100% (3 of 3)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Metrics

- - - - - -
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
TestMethod1()10100%100%1
-

File(s)

-

E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\UnitTest1.cs

- - - - - - - - - - - - - - - - - - - -
#LineLine coverage
 1using System;
 2using Microsoft.VisualStudio.TestTools.UnitTesting;
 3
 4namespace UnitTestProject2
 5{
 6    [TestClass]
 7    public class UnitTest1
 8    {
 9        [TestMethod]
 10        public void TestMethod1()
 111        {
 112            Assert.AreEqual(1,1);
 113        }
 14    }
 15}
-
-
-
-

Methods/Properties

-TestMethod1()
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.png deleted file mode 100644 index 1d4585190f78c440e6cda1f35623d1780d05fbd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2925 zcmV-z3zGDSP)4*Bbt2V+UCULEJzD1zd5%y@;aL*s6%J_)O5c zMH`=0TcWmr#>6Ey#ywUOHBGv>)C~+G&jQL5P+Ursry}?e*})NkVU~CPgLj+-hM}U3 zPk(a4%vt{P-+R9M{ogrvq!AGjmKGKk2>`xer3*gVTd~-*v<~$bajZk#y-h0-L3FIL zu5yD^CX*#-G@3PkcSQg1#_n+k`|D75Z_~PHpjqo=fNZ6RKvN_0bT{2VfWnnT?YML+XoCbZVYh$Ah3BDHi%g1!vHokJRD-NSX-}F zYan;3hupg!h5bw6mG1_>Y;Q=LB=0ir_tHLeurc$H8r}pk`~LZzJLrDx8cN5HhrP42 zwoUp%3YmpW`@Jlv1`Qqx1fT5%$w$DMw2xq8*fOjb_vjIZJbMPW=g%SCy&IO6maw(8 z{fhxr>Vn*yiMoFXE#y+OYD#fq@5bcj8?f9()*{)}Gq7FMEE8 z%VRDhd2}+OyS1vCxLJrTaANog_}lwy>!x~4Mc%|bq>N5MZ@b>w`t-5s7~wL)>=^Z! z&85B`lZuKUku2n;rQ3W`@ib${yzSScZdxfxp+E2_P3jFW_YHNlZ?kjBopV z%Yip7V;Uy8PeN>;SbTQvGswg;oE~u+1$6~Tt1#XjJ6ky8(BMM|>l}vf^1jo?9N#w{ zk^hcFh*Jnw`L4p!n@cg!aUk3*-Eg(ys@d0|2ex61(JVzwu}NO1#6kP_qhRo0_yz|< z%7E4q*vKk}*1`T!+Z%=@OP0XN$qA7Rz%O3BU^-BZ$&)9;-rin&an{z>oKXpk<{E*x zQ>RX-sHiZRvgko_badqNl7GX#&P+BmHa4QJuFhyI#jQ{%T5M~}4ChH_*ZU@>>u!SE z1k|hQk&u@_LL9MoV-e~S%IEa6^FwY;E^?}Kc;DRGTm;w$;E(b@^!(XKY;b+Tb^KWH zBZfH-BSo)`+1Jh&g>{ASu=K#ET|VXYKM(sEk(rV0k_UQV3uZT8Dv=m=fYbNiL;1@u ztc@|7tYxq%d{j_NG!Q9UpUigs9^K=MgIr+eb+>WcmBK}$_dg{P+{)~#EI z=ZuZq+}x0zosC0>4w-$E$=;@B8{jwqSyfs1#{U~k?=l^kRhfv-i|2EmEmabj9qN=J&%M3z2fs8;* zfT2FKX3fI<`SX#Lm4$ip=Ao>t4A-w;$Cxo=czTOf+8bPk^kftkn7>@D3k7oHH41 zdM+|eN__(Z1NmG6+X~|;?e^=}53^^_=Bb~I#(8;pp`f6k)oV4nHwM=`_T@{n$C6#K z(bvPe?g%`0Ywu#ioelhZUTq$VrWL_a=7?><+mKwEjJQAJc-!Wn&Dh?5JEE>dnT{8) z*Rry)5}Wfj^Uo9AC*rp!zoA}PkHj&F*nWRIlEx%q*rj2pZK^ftQx9x4R#xcB^opU? zvXrwkt}CQ=CXP4MY+VmslfAQmEk^ytw8;o;#N z7{VB0q!TAjG~XoFmo%65X=46_vqECE(9lqxIw_SdSg=43n8dKU@$06Nef#!trX-{E zF3e%B8TS2`^_v}t>$5(x1`LI18YRs`jI?UiD*kptsHgNusVO%%7o$gy=5ta#eZFzyMsqq~b*E09;;D?7 zic%ug=j7yI#E20b0HnQWj-EYxwg?(Zzj=9iTn~~a^Y!)Bw$nT_X3XH=>)W?4*LXa= zuvB{c_H7;u`KLncNxuH>^eL1x#b@Ach$ViWF8s}jvaXT@FCY= zB)$m{4Gj%kmg zmKugF{kPy?$U&U`?`cG5M)Ns0{ke&!#H6uFP-#>+`}8cf6YOhlj}rX%(ukWu<1_8=CaBM~7D zQp%({>PrhQ*h{8=2pHs#07U&sSJ6(CKqd4gpio}|BrzlTqq*q4FMSr`qA|yhALjt0 zbW7tXE-NN8C@w(*N?OskEt%o3K0bK`WOFUWKr6rNUwshRY$fcTD&>0hbeR0C;h9-J zj9ynAba6p1c5}U%KG_!M1F?q{OrG)AYM|Wl0-7eZ*?|)f$Y|kdv4u?s#X@cLpY)L+ z>V+7I8J%CfKGThx06>NO(Rk`d?F35Qz5)nq8G8$DLOX35=&+f<4xhj|&d00000NkvXXu0mjf72v4t diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg deleted file mode 100644 index f3f4a54c..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/badge_linecoverage.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - Code coverage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Generated by: ReportGenerator 4.1.2.0 - - - - Coverage - Coverage - 68.1%68.1% - - - - - Line coverage - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js deleted file mode 100644 index b82bca96..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/class.js +++ /dev/null @@ -1,207 +0,0 @@ -/* Chartist.js 0.11.0 - * Copyright © 2017 Gion Kunz - * Free to use under either the WTFPL license or the MIT license. - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT - */ - -!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { - var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { - "use strict"; function d(a) { - var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { - var i = c.serialMap(b.normalized.series, function () { - return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) - }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") - } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) - } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) - }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a -}); - -var i, l, selectedLine = null; - -/* Navigate to hash without browser history entry */ -var navigateToHash = function () { - if (window.history !== undefined && window.history.replaceState !== undefined) { - window.history.replaceState(undefined, undefined, this.getAttribute("href")); - } -}; - -var hashLinks = document.getElementsByClassName('navigatetohash'); -for (i = 0, l = hashLinks.length; i < l; i++) { - hashLinks[i].addEventListener('click', navigateToHash); -} - -/* Switch test method */ -var switchTestMethod = function () { - var method = this.getAttribute("value"); - console.log("Selected test method: " + method); - - var lines, i, l, coverageData, lineAnalysis, cells; - - lines = document.querySelectorAll('.lineAnalysis tr'); - - for (i = 1, l = lines.length; i < l; i++) { - coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); - lineAnalysis = coverageData[method]; - cells = lines[i].querySelectorAll('td'); - if (lineAnalysis === undefined) { - lineAnalysis = coverageData.AllTestMethods; - if (lineAnalysis.LVS !== 'gray') { - cells[0].setAttribute('class', 'red'); - cells[1].innerText = cells[1].textContent = '0'; - cells[4].setAttribute('class', 'lightred'); - } - } else { - cells[0].setAttribute('class', lineAnalysis.LVS); - cells[1].innerText = cells[1].textContent = lineAnalysis.VC; - cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); - } - } -}; - -var testMethods = document.getElementsByClassName('switchtestmethod'); -for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].addEventListener('change', switchTestMethod); -} - -/* Highlight test method by line */ -var toggleLine = function () { - if (selectedLine === this) { - selectedLine = null; - } else { - selectedLine = null; - unhighlightTestMethods(); - highlightTestMethods.call(this); - selectedLine = this; - } - -}; -var highlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var lineAnalysis; - var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); - var testMethods = document.getElementsByClassName('testmethod'); - - for (i = 0, l = testMethods.length; i < l; i++) { - lineAnalysis = coverageData[testMethods[i].id]; - if (lineAnalysis === undefined) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } else { - testMethods[i].className += ' light' + lineAnalysis.LVS; - } - } -}; -var unhighlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var testMethods = document.getElementsByClassName('testmethod'); - for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } -}; -var coverableLines = document.getElementsByClassName('coverableline'); -for (i = 0, l = coverableLines.length; i < l; i++) { - coverableLines[i].addEventListener('click', toggleLine); - coverableLines[i].addEventListener('mouseenter', highlightTestMethods); - coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); -} - -/* History charts */ -var renderChart = function (chart) { - // Remove current children (e.g. PNG placeholder) - while (chart.firstChild) { - chart.firstChild.remove(); - } - - var chartData = window[chart.getAttribute('data-data')]; - var options = { - axisY: { - type: undefined, - onlyInteger: true - }, - lineSmooth: false, - low: 0, - high: 100, - scaleMinSpace: 20, - onlyInteger: true, - fullWidth: true - }; - var lineChart = new Chartist.Line(chart, { - labels: [], - series: chartData.series - }, options); - - /* Zoom */ - var zoomButtonDiv = document.createElement("div"); - zoomButtonDiv.className = "toggleZoom"; - var zoomButtonLink = document.createElement("a"); - zoomButtonLink.setAttribute("href", ""); - var zoomButtonText = document.createElement("i"); - zoomButtonText.className = "icon-search-plus"; - - zoomButtonLink.appendChild(zoomButtonText); - zoomButtonDiv.appendChild(zoomButtonLink); - - chart.appendChild(zoomButtonDiv); - - zoomButtonDiv.addEventListener('click', function (event) { - event.preventDefault(); - - if (options.axisY.type === undefined) { - options.axisY.type = Chartist.AutoScaleAxis; - zoomButtonText.className = "icon-search-minus"; - } else { - options.axisY.type = undefined; - zoomButtonText.className = "icon-search-plus"; - } - - lineChart.update(null, options); - }); - - var tooltip = document.createElement("div"); - tooltip.className = "tooltip"; - - chart.appendChild(tooltip); - - /* Tooltips */ - var showToolTip = function () { - var point = this; - var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); - - tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; - tooltip.style.display = 'block'; - }; - - var moveToolTip = function (event) { - var box = chart.getBoundingClientRect(); - var left = event.pageX - box.left - window.pageXOffset; - var top = event.pageY - box.top - window.pageYOffset; - - tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; - tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; - }; - - var hideToolTip = function () { - tooltip.style.display = 'none'; - }; - - chart.addEventListener('mousemove', moveToolTip); - - lineChart.on('created', function () { - var chartPoints = chart.getElementsByClassName('ct-point'); - for (i = 0, l = chartPoints.length; i < l; i++) { - chartPoints[i].addEventListener('mousemove', showToolTip); - chartPoints[i].addEventListener('mouseout', hideToolTip); - } - }); -}; - -var charts = document.getElementsByClassName('historychart'); -for (i = 0, l = charts.length; i < l; i++) { - renderChart(charts[i]); -} \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml deleted file mode 100644 index dbfc7f84..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/coverage.xml +++ /dev/null @@ -1,1270 +0,0 @@ - - - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll - 2021-02-09T23:13:42Z - mscorlib - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe - 2021-03-10T01:18:39.6988681Z - vstest.console - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Client.dll - 2021-03-10T01:18:39.3608699Z - Microsoft.VisualStudio.TestPlatform.Client - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Common.dll - 2021-03-10T01:18:39.3648679Z - Microsoft.VisualStudio.TestPlatform.Common - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.Utilities.dll - 2021-03-10T01:18:39.1838668Z - Microsoft.TestPlatform.Utilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\NuGet.Frameworks.dll - 2021-03-10T01:18:39.455872Z - NuGet.Frameworks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.Extensions.FileSystemGlobbing.dll - 2021-03-10T01:18:39.1398684Z - Microsoft.Extensions.FileSystemGlobbing - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CrossPlatEngine.dll - 2021-03-10T01:18:39.1798685Z - Microsoft.TestPlatform.CrossPlatEngine - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.Cci.dll - 2021-03-10T01:18:40.1568669Z - Microsoft.Cci - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.InteropServices\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.InteropServices.dll - 2019-12-07T09:10:35.9300981Z - System.Runtime.InteropServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.BlameDataCollector.dll - 2021-03-10T01:18:38.7708669Z - Microsoft.TestPlatform.Extensions.BlameDataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.Extensions.EventLogCollector.dll - 2021-03-10T01:18:38.7728666Z - Microsoft.TestPlatform.Extensions.EventLogCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.TestPlatform.TestHostRuntimeProvider.dll - 2021-03-10T01:18:38.7748692Z - Microsoft.TestPlatform.TestHostRuntimeProvider - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.ArchitectureTools.PEReader.dll - 2021-03-10T01:18:39.2018677Z - Microsoft.VisualStudio.ArchitectureTools.PEReader - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Coverage.Interprocess.dll - 2021-03-10T01:18:38.7888677Z - Microsoft.VisualStudio.Coverage.Interprocess - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\netstandard\v4.0_2.0.0.0__cc7b13ffcd2ddd51\netstandard.dll - 2019-12-07T09:10:37.6330785Z - netstandard - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ValueTuple\v4.0_4.0.0.0__cc7b13ffcd2ddd51\System.ValueTuple.dll - 2019-12-07T09:10:37.742541Z - System.ValueTuple - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.Fakes.DataCollector.dll - 2021-03-10T01:18:40.1588666Z - Microsoft.VisualStudio.Fakes.DataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter.dll - 2021-03-10T01:18:38.7898664Z - Microsoft.VisualStudio.TestPlatform.Extensions.CodedWebTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter.dll - 2021-03-10T01:18:38.7968662Z - Microsoft.VisualStudio.TestPlatform.Extensions.TmiAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Common.dll - 2021-03-10T01:18:39.2388684Z - Microsoft.VisualStudio.QualityTools.Common - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter.dll - 2021-03-10T01:18:38.7918661Z - Microsoft.VisualStudio.TestPlatform.Extensions.GenericTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger.dll - 2021-03-10T01:18:38.792866Z - Microsoft.VisualStudio.TestPlatform.Extensions.Html.TestLogger - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter.dll - 2021-03-10T01:18:38.7948677Z - Microsoft.VisualStudio.TestPlatform.Extensions.OrderedTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger.dll - 2021-03-10T01:18:38.7998656Z - Microsoft.VisualStudio.TestPlatform.Extensions.Trx.TestLogger - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration.dll - 2021-03-10T01:18:38.8038671Z - Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll - 2021-03-10T01:18:39.3488657Z - Microsoft.VisualStudio.QualityTools.UnitTestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter.dll - 2021-03-10T01:18:38.8048665Z - Microsoft.VisualStudio.TestPlatform.Extensions.WebTestAdapter - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces.dll - 2021-03-10T01:18:38.8068659Z - Microsoft.VisualStudio.TestTools.CppUnitTestFramework.ComInterfaces - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension.dll - 2021-03-10T01:18:38.8078663Z - Microsoft.VisualStudio.TestTools.CppUnitTestFramework.CppUnitTestExtension - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model.dll - 2021-03-10T01:18:38.8098657Z - Microsoft.VisualStudio.TestTools.DataCollection.MediaRecorder.Model - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector.dll - 2021-03-10T01:18:38.8118659Z - Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorderCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Extensions\Microsoft.VisualStudio.TraceDataCollector.dll - 2021-03-10T01:18:38.8168685Z - Microsoft.VisualStudio.TraceDataCollector - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.IntelliTrace.Core.dll - 2021-03-10T01:18:39.1678662Z - Microsoft.IntelliTrace.Core - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Reflection.Metadata.dll - 2021-03-10T01:18:39.5908676Z - System.Reflection.Metadata - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\System.Collections.Immutable.dll - 2021-03-10T01:18:39.5778692Z - System.Collections.Immutable - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.FileSystem\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.FileSystem.dll - 2019-12-07T09:10:36.0095245Z - System.IO.FileSystem - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO.MemoryMappedFiles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.MemoryMappedFiles.dll - 2019-12-07T09:10:34.5552678Z - System.IO.MemoryMappedFiles - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Handles\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Handles.dll - 2019-12-07T09:10:37.6643716Z - System.Runtime.Handles - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.dll - 2019-12-07T09:10:36.0705812Z - System.Text.Encoding - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.Encoding.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.Encoding.Extensions.dll - 2019-12-07T09:10:36.1495949Z - System.Text.Encoding.Extensions - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.Fakes.dll - 2021-03-10T01:18:40.1608669Z - Microsoft.VisualStudio.TestPlatform.Fakes - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CommunicationUtilities.dll - 2021-03-10T01:18:39.1748663Z - Microsoft.TestPlatform.CommunicationUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Newtonsoft.Json.dll - 2021-03-10T01:18:39.451869Z - Newtonsoft.Json - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll - 2019-12-07T09:10:37.6960843Z - System.Resources.ResourceManager - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ComponentModel.Composition\v4.0_4.0.0.0__b77a5c561934e089\System.ComponentModel.Composition.dll - 2019-12-07T09:10:36.1023351Z - System.ComponentModel.Composition - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.ReaderWriter\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.ReaderWriter.dll - 2019-12-07T09:10:34.5705678Z - System.Xml.ReaderWriter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.XDocument\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Xml.XDocument.dll - 2019-12-07T09:10:34.5552678Z - System.Xml.XDocument - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll - 2019-12-07T09:10:35.962301Z - System.Xml.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\UnitTestProject1.dll - 2019-04-19T07:50:12.1584913Z - UnitTestProject1 - - - - - - - <Module> - - - - - UnitTestProject1.UnitTest1 - - - - 100663297 - System.Void UnitTestProject1.UnitTest1::TestMethod1() - - - - - - - - - - - - - - 100663298 - System.Void UnitTestProject1.UnitTest1::TestMethod2() - - - - - - - - - - - - - - 100663299 - System.Void UnitTestProject1.UnitTest1::.ctor() - - - - - - - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll - 2020-08-05T02:39:55.9298663Z - System.Runtime.Serialization - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject2\bin\debug\UnitTestProject2.dll - 2019-04-19T07:50:12.1584913Z - UnitTestProject2 - - - - - - - <Module> - - - - - UnitTestProject2.UnitTest1 - - - - 100663297 - System.Void UnitTestProject2.UnitTest1::TestMethod1() - - - - - - - - - - - - 100663298 - System.Void UnitTestProject2.UnitTest1::.ctor() - - - - - - - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll - 2020-08-05T02:39:55.9298663Z - System.Runtime.Serialization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections.Concurrent\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.Concurrent.dll - 2019-12-07T09:10:34.5552678Z - System.Collections.Concurrent - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\ClassLibrary1.dll - 2019-04-19T06:37:47.6105603Z - ClassLibrary1 - - - - - - - <Module> - - - - - ClassLibrary1.Calculation - - - - 100663297 - System.Int32 ClassLibrary1.Calculation::Add(System.Int32,System.Int32) - - - - - - - - - - - - 100663298 - System.Int32 ClassLibrary1.Calculation::Sub(System.Int32,System.Int32) - - - - - - - - - - - - 100663299 - System.Int32 ClassLibrary1.Calculation::Sub1(System.Int32,System.Int32) - - - - - - - - - - - - 100663300 - System.Void ClassLibrary1.Calculation::.ctor() - - - - - - - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Resources.ResourceManager\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Resources.ResourceManager.dll - 2019-12-07T09:10:37.6960843Z - System.Resources.ResourceManager - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Text.RegularExpressions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Text.RegularExpressions.dll - 2019-12-07T09:10:34.4610177Z - System.Text.RegularExpressions - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.CoreUtilities.dll - 2021-03-10T01:18:39.1758657Z - Microsoft.TestPlatform.CoreUtilities - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.TestPlatform.PlatformAbstractions.dll - 2021-03-10T01:18:39.1818661Z - Microsoft.TestPlatform.PlatformAbstractions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - 2018-06-05T04:54:38Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll - 2020-10-08T02:21:33.8194762Z - System - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - 2018-06-05T04:51:36Z - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll - 2019-12-07T09:10:34.4923358Z - System.Runtime - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - 2018-06-05T04:47:02Z - Microsoft.VisualStudio.TestPlatform.TestFramework - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Collections\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Collections.dll - 2019-12-07T09:10:34.5705678Z - System.Collections - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.dll - 2019-12-07T09:10:36.0237545Z - System.Reflection - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Extensions.dll - 2019-12-07T09:10:37.6174386Z - System.Runtime.Extensions - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.dll - 2018-06-05T04:50:06Z - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll - 2019-12-07T09:10:36.086204Z - System.Linq - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll - 2021-02-09T23:13:40.5754059Z - System.Core - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Globalization\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Globalization.dll - 2019-12-07T09:10:36.086204Z - System.Globalization - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.IO\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.IO.dll - 2019-12-07T09:10:34.5552678Z - System.IO - - - - E:\src\sample.dotblog\CodeCoverage\OpenCover\Lab.OpenCoverDemo\UnitTestProject1\bin\debug\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - 2018-06-05T04:53:44Z - Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll - 2020-09-04T22:37:08Z - System.Data - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.dll - 2019-12-07T09:10:34.5860246Z - System.Threading - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Reflection.Extensions\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Reflection.Extensions.dll - 2019-12-07T09:10:37.7113237Z - System.Reflection.Extensions - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll - 2019-12-07T09:10:34.5705678Z - System.Threading.Tasks - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll - 2020-06-05T05:04:05.335002Z - System.Configuration - - - - C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll - 2019-12-07T09:10:37.7737543Z - System.Xml - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.ExecutionCommon.dll - 2021-03-10T01:18:39.2588672Z - Microsoft.VisualStudio.QualityTools.ExecutionCommon - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.Resource.dll - 2021-03-10T01:18:39.3078688Z - Microsoft.VisualStudio.QualityTools.Resource - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.QualityTools.WebTestFramework.dll - 2021-03-10T01:18:39.3588667Z - Microsoft.VisualStudio.QualityTools.WebTestFramework - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\TestPlatform\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll - 2021-03-10T01:18:39.3688664Z - Microsoft.VisualStudio.TestPlatform.ObjectModel - - - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg deleted file mode 100644 index 11b5cabf..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_cube.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg deleted file mode 100644 index d11cf041..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_down-dir_active.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg deleted file mode 100644 index f0148b3a..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_fork.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg deleted file mode 100644 index 252166bb..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_info-circled.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg deleted file mode 100644 index 3c30c365..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_minus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg deleted file mode 100644 index 79327232..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_plus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg deleted file mode 100644 index c174eb5e..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-minus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg deleted file mode 100644 index 04b24ecc..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_search-plus.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg deleted file mode 100644 index 567c11f3..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg deleted file mode 100644 index bb225544..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_up-dir_active.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg deleted file mode 100644 index 0e9a8601..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/icon_wrench.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm deleted file mode 100644 index f2f23f7e..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/index.htm +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - -Summary - Coverage Report - -
-

Summary

- ---- - - - - - - - - - - - - - - - -
Generated on:2021/3/26 - 下午 04:34:49
Parser:OpenCoverParser
Assemblies:3
Classes:3
Files:3
Covered lines:15
Uncovered lines:7
Coverable lines:22
Total lines:59
Line coverage:68.1% (15 of 22)
Covered branches:0
Total branches:0
Tag:Build.2019.11.11
-

Coverage History

-
- -

Risk Hotspots

- - -

No risk hotspots found.

-

Coverage

- - ----------- - - - - - - - - - -
NameCoveredUncoveredCoverableTotalLine coverageBranch coverage
ClassLibrary13691833.3%
  
 
ClassLibrary1.Calculation3691833.3%
  
 
UnitTestProject191102690%
  
 
UnitTestProject1.UnitTest191102690%
  
 
UnitTestProject230315100%
 
 
UnitTestProject2.UnitTest130315100%
 
 
-
-
- - \ No newline at end of file diff --git a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js b/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js deleted file mode 100644 index 160252eb..00000000 --- a/CodeCoverage/OpenCover/Lab.OpenCoverDemo/report/main.js +++ /dev/null @@ -1,274 +0,0 @@ -/* Chartist.js 0.11.0 - * Copyright © 2017 Gion Kunz - * Free to use under either the WTFPL license or the MIT license. - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL - * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT - */ - -!function (a, b) { "function" == typeof define && define.amd ? define("Chartist", [], function () { return a.Chartist = b() }) : "object" == typeof module && module.exports ? module.exports = b() : a.Chartist = b() }(this, function () { - var a = { version: "0.11.0" }; return function (a, b, c) { "use strict"; c.namespaces = { svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns/", xhtml: "http://www.w3.org/1999/xhtml", xlink: "http://www.w3.org/1999/xlink", ct: "http://gionkunz.github.com/chartist-js/ct" }, c.noop = function (a) { return a }, c.alphaNumerate = function (a) { return String.fromCharCode(97 + a % 26) }, c.extend = function (a) { var b, d, e; for (a = a || {}, b = 1; b < arguments.length; b++) { d = arguments[b]; for (var f in d) e = d[f], "object" != typeof e || null === e || e instanceof Array ? a[f] = e : a[f] = c.extend(a[f], e) } return a }, c.replaceAll = function (a, b, c) { return a.replace(new RegExp(b, "g"), c) }, c.ensureUnit = function (a, b) { return "number" == typeof a && (a += b), a }, c.quantity = function (a) { if ("string" == typeof a) { var b = /^(\d+)\s*(.*)$/g.exec(a); return { value: +b[1], unit: b[2] || void 0 } } return { value: a } }, c.querySelector = function (a) { return a instanceof Node ? a : b.querySelector(a) }, c.times = function (a) { return Array.apply(null, new Array(a)) }, c.sum = function (a, b) { return a + (b ? b : 0) }, c.mapMultiply = function (a) { return function (b) { return b * a } }, c.mapAdd = function (a) { return function (b) { return b + a } }, c.serialMap = function (a, b) { var d = [], e = Math.max.apply(null, a.map(function (a) { return a.length })); return c.times(e).forEach(function (c, e) { var f = a.map(function (a) { return a[e] }); d[e] = b.apply(null, f) }), d }, c.roundWithPrecision = function (a, b) { var d = Math.pow(10, b || c.precision); return Math.round(a * d) / d }, c.precision = 8, c.escapingMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, c.serialize = function (a) { return null === a || void 0 === a ? a : ("number" == typeof a ? a = "" + a : "object" == typeof a && (a = JSON.stringify({ data: a })), Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, b, c.escapingMap[b]) }, a)) }, c.deserialize = function (a) { if ("string" != typeof a) return a; a = Object.keys(c.escapingMap).reduce(function (a, b) { return c.replaceAll(a, c.escapingMap[b], b) }, a); try { a = JSON.parse(a), a = void 0 !== a.data ? a.data : a } catch (b) { } return a }, c.createSvg = function (a, b, d, e) { var f; return b = b || "100%", d = d || "100%", Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function (a) { return a.getAttributeNS(c.namespaces.xmlns, "ct") }).forEach(function (b) { a.removeChild(b) }), f = new c.Svg("svg").attr({ width: b, height: d }).addClass(e), f._node.style.width = b, f._node.style.height = d, a.appendChild(f._node), f }, c.normalizeData = function (a, b, d) { var e, f = { raw: a, normalized: {} }; return f.normalized.series = c.getDataArray({ series: a.series || [] }, b, d), e = f.normalized.series.every(function (a) { return a instanceof Array }) ? Math.max.apply(null, f.normalized.series.map(function (a) { return a.length })) : f.normalized.series.length, f.normalized.labels = (a.labels || []).slice(), Array.prototype.push.apply(f.normalized.labels, c.times(Math.max(0, e - f.normalized.labels.length)).map(function () { return "" })), b && c.reverseData(f.normalized), f }, c.safeHasProperty = function (a, b) { return null !== a && "object" == typeof a && a.hasOwnProperty(b) }, c.isDataHoleValue = function (a) { return null === a || void 0 === a || "number" == typeof a && isNaN(a) }, c.reverseData = function (a) { a.labels.reverse(), a.series.reverse(); for (var b = 0; b < a.series.length; b++)"object" == typeof a.series[b] && void 0 !== a.series[b].data ? a.series[b].data.reverse() : a.series[b] instanceof Array && a.series[b].reverse() }, c.getDataArray = function (a, b, d) { function e(a) { if (c.safeHasProperty(a, "value")) return e(a.value); if (c.safeHasProperty(a, "data")) return e(a.data); if (a instanceof Array) return a.map(e); if (!c.isDataHoleValue(a)) { if (d) { var b = {}; return "string" == typeof d ? b[d] = c.getNumberOrUndefined(a) : b.y = c.getNumberOrUndefined(a), b.x = a.hasOwnProperty("x") ? c.getNumberOrUndefined(a.x) : b.x, b.y = a.hasOwnProperty("y") ? c.getNumberOrUndefined(a.y) : b.y, b } return c.getNumberOrUndefined(a) } } return a.series.map(e) }, c.normalizePadding = function (a, b) { return b = b || 0, "number" == typeof a ? { top: a, right: a, bottom: a, left: a } : { top: "number" == typeof a.top ? a.top : b, right: "number" == typeof a.right ? a.right : b, bottom: "number" == typeof a.bottom ? a.bottom : b, left: "number" == typeof a.left ? a.left : b } }, c.getMetaData = function (a, b) { var c = a.data ? a.data[b] : a[b]; return c ? c.meta : void 0 }, c.orderOfMagnitude = function (a) { return Math.floor(Math.log(Math.abs(a)) / Math.LN10) }, c.projectLength = function (a, b, c) { return b / c.range * a }, c.getAvailableHeight = function (a, b) { return Math.max((c.quantity(b.height).value || a.height()) - (b.chartPadding.top + b.chartPadding.bottom) - b.axisX.offset, 0) }, c.getHighLow = function (a, b, d) { function e(a) { if (void 0 !== a) if (a instanceof Array) for (var b = 0; b < a.length; b++)e(a[b]); else { var c = d ? +a[d] : +a; g && c > f.high && (f.high = c), h && c < f.low && (f.low = c) } } b = c.extend({}, b, d ? b["axis" + d.toUpperCase()] : {}); var f = { high: void 0 === b.high ? -Number.MAX_VALUE : +b.high, low: void 0 === b.low ? Number.MAX_VALUE : +b.low }, g = void 0 === b.high, h = void 0 === b.low; return (g || h) && e(a), (b.referenceValue || 0 === b.referenceValue) && (f.high = Math.max(b.referenceValue, f.high), f.low = Math.min(b.referenceValue, f.low)), f.high <= f.low && (0 === f.low ? f.high = 1 : f.low < 0 ? f.high = 0 : f.high > 0 ? f.low = 0 : (f.high = 1, f.low = 0)), f }, c.isNumeric = function (a) { return null !== a && isFinite(a) }, c.isFalseyButZero = function (a) { return !a && 0 !== a }, c.getNumberOrUndefined = function (a) { return c.isNumeric(a) ? +a : void 0 }, c.isMultiValue = function (a) { return "object" == typeof a && ("x" in a || "y" in a) }, c.getMultiValue = function (a, b) { return c.isMultiValue(a) ? c.getNumberOrUndefined(a[b || "y"]) : c.getNumberOrUndefined(a) }, c.rho = function (a) { function b(a, c) { return a % c === 0 ? c : b(c, a % c) } function c(a) { return a * a + 1 } if (1 === a) return a; var d, e = 2, f = 2; if (a % 2 === 0) return 2; do e = c(e) % a, f = c(c(f)) % a, d = b(Math.abs(e - f), a); while (1 === d); return d }, c.getBounds = function (a, b, d, e) { function f(a, b) { return a === (a += b) && (a *= 1 + (b > 0 ? o : -o)), a } var g, h, i, j = 0, k = { high: b.high, low: b.low }; k.valueRange = k.high - k.low, k.oom = c.orderOfMagnitude(k.valueRange), k.step = Math.pow(10, k.oom), k.min = Math.floor(k.low / k.step) * k.step, k.max = Math.ceil(k.high / k.step) * k.step, k.range = k.max - k.min, k.numberOfSteps = Math.round(k.range / k.step); var l = c.projectLength(a, k.step, k), m = l < d, n = e ? c.rho(k.range) : 0; if (e && c.projectLength(a, 1, k) >= d) k.step = 1; else if (e && n < k.step && c.projectLength(a, n, k) >= d) k.step = n; else for (; ;) { if (m && c.projectLength(a, k.step, k) <= d) k.step *= 2; else { if (m || !(c.projectLength(a, k.step / 2, k) >= d)) break; if (k.step /= 2, e && k.step % 1 !== 0) { k.step *= 2; break } } if (j++ > 1e3) throw new Error("Exceeded maximum number of iterations while optimizing scale step!") } var o = 2.221e-16; for (k.step = Math.max(k.step, o), h = k.min, i = k.max; h + k.step <= k.low;)h = f(h, k.step); for (; i - k.step >= k.high;)i = f(i, -k.step); k.min = h, k.max = i, k.range = k.max - k.min; var p = []; for (g = k.min; g <= k.max; g = f(g, k.step)) { var q = c.roundWithPrecision(g); q !== p[p.length - 1] && p.push(q) } return k.values = p, k }, c.polarToCartesian = function (a, b, c, d) { var e = (d - 90) * Math.PI / 180; return { x: a + c * Math.cos(e), y: b + c * Math.sin(e) } }, c.createChartRect = function (a, b, d) { var e = !(!b.axisX && !b.axisY), f = e ? b.axisY.offset : 0, g = e ? b.axisX.offset : 0, h = a.width() || c.quantity(b.width).value || 0, i = a.height() || c.quantity(b.height).value || 0, j = c.normalizePadding(b.chartPadding, d); h = Math.max(h, f + j.left + j.right), i = Math.max(i, g + j.top + j.bottom); var k = { padding: j, width: function () { return this.x2 - this.x1 }, height: function () { return this.y1 - this.y2 } }; return e ? ("start" === b.axisX.position ? (k.y2 = j.top + g, k.y1 = Math.max(i - j.bottom, k.y2 + 1)) : (k.y2 = j.top, k.y1 = Math.max(i - j.bottom - g, k.y2 + 1)), "start" === b.axisY.position ? (k.x1 = j.left + f, k.x2 = Math.max(h - j.right, k.x1 + 1)) : (k.x1 = j.left, k.x2 = Math.max(h - j.right - f, k.x1 + 1))) : (k.x1 = j.left, k.x2 = Math.max(h - j.right, k.x1 + 1), k.y2 = j.top, k.y1 = Math.max(i - j.bottom, k.y2 + 1)), k }, c.createGrid = function (a, b, d, e, f, g, h, i) { var j = {}; j[d.units.pos + "1"] = a, j[d.units.pos + "2"] = a, j[d.counterUnits.pos + "1"] = e, j[d.counterUnits.pos + "2"] = e + f; var k = g.elem("line", j, h.join(" ")); i.emit("draw", c.extend({ type: "grid", axis: d, index: b, group: g, element: k }, j)) }, c.createGridBackground = function (a, b, c, d) { var e = a.elem("rect", { x: b.x1, y: b.y2, width: b.width(), height: b.height() }, c, !0); d.emit("draw", { type: "gridBackground", group: a, element: e }) }, c.createLabel = function (a, d, e, f, g, h, i, j, k, l, m) { var n, o = {}; if (o[g.units.pos] = a + i[g.units.pos], o[g.counterUnits.pos] = i[g.counterUnits.pos], o[g.units.len] = d, o[g.counterUnits.len] = Math.max(0, h - 10), l) { var p = b.createElement("span"); p.className = k.join(" "), p.setAttribute("xmlns", c.namespaces.xhtml), p.innerText = f[e], p.style[g.units.len] = Math.round(o[g.units.len]) + "px", p.style[g.counterUnits.len] = Math.round(o[g.counterUnits.len]) + "px", n = j.foreignObject(p, c.extend({ style: "overflow: visible;" }, o)) } else n = j.elem("text", o, k.join(" ")).text(f[e]); m.emit("draw", c.extend({ type: "label", axis: g, index: e, group: j, element: n, text: f[e] }, o)) }, c.getSeriesOption = function (a, b, c) { if (a.name && b.series && b.series[a.name]) { var d = b.series[a.name]; return d.hasOwnProperty(c) ? d[c] : b[c] } return b[c] }, c.optionsProvider = function (b, d, e) { function f(b) { var f = h; if (h = c.extend({}, j), d) for (i = 0; i < d.length; i++) { var g = a.matchMedia(d[i][0]); g.matches && (h = c.extend(h, d[i][1])) } e && b && e.emit("optionsChanged", { previousOptions: f, currentOptions: h }) } function g() { k.forEach(function (a) { a.removeListener(f) }) } var h, i, j = c.extend({}, b), k = []; if (!a.matchMedia) throw "window.matchMedia not found! Make sure you're using a polyfill."; if (d) for (i = 0; i < d.length; i++) { var l = a.matchMedia(d[i][0]); l.addListener(f), k.push(l) } return f(), { removeMediaQueryListeners: g, getCurrentOptions: function () { return c.extend({}, h) } } }, c.splitIntoSegments = function (a, b, d) { var e = { increasingX: !1, fillHoles: !1 }; d = c.extend({}, e, d); for (var f = [], g = !0, h = 0; h < a.length; h += 2)void 0 === c.getMultiValue(b[h / 2].value) ? d.fillHoles || (g = !0) : (d.increasingX && h >= 2 && a[h] <= a[h - 2] && (g = !0), g && (f.push({ pathCoordinates: [], valueData: [] }), g = !1), f[f.length - 1].pathCoordinates.push(a[h], a[h + 1]), f[f.length - 1].valueData.push(b[h / 2])); return f } }(window, document, a), function (a, b, c) { "use strict"; c.Interpolation = {}, c.Interpolation.none = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e = new c.Svg.Path, f = !0, g = 0; g < b.length; g += 2) { var h = b[g], i = b[g + 1], j = d[g / 2]; void 0 !== c.getMultiValue(j.value) ? (f ? e.move(h, i, !1, j) : e.line(h, i, !1, j), f = !1) : a.fillHoles || (f = !0) } return e } }, c.Interpolation.simple = function (a) { var b = { divisor: 2, fillHoles: !1 }; a = c.extend({}, b, a); var d = 1 / Math.max(1, a.divisor); return function (b, e) { for (var f, g, h, i = new c.Svg.Path, j = 0; j < b.length; j += 2) { var k = b[j], l = b[j + 1], m = (k - f) * d, n = e[j / 2]; void 0 !== n.value ? (void 0 === h ? i.move(k, l, !1, n) : i.curve(f + m, g, k - m, l, k, l, !1, n), f = k, g = l, h = n) : a.fillHoles || (f = k = h = void 0) } return i } }, c.Interpolation.cardinal = function (a) { var b = { tension: 1, fillHoles: !1 }; a = c.extend({}, b, a); var d = Math.min(1, Math.max(0, a.tension)), e = 1 - d; return function f(b, g) { var h = c.splitIntoSegments(b, g, { fillHoles: a.fillHoles }); if (h.length) { if (h.length > 1) { var i = []; return h.forEach(function (a) { i.push(f(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(i) } if (b = h[0].pathCoordinates, g = h[0].valueData, b.length <= 4) return c.Interpolation.none()(b, g); for (var j, k = (new c.Svg.Path).move(b[0], b[1], !1, g[0]), l = 0, m = b.length; m - 2 * !j > l; l += 2) { var n = [{ x: +b[l - 2], y: +b[l - 1] }, { x: +b[l], y: +b[l + 1] }, { x: +b[l + 2], y: +b[l + 3] }, { x: +b[l + 4], y: +b[l + 5] }]; j ? l ? m - 4 === l ? n[3] = { x: +b[0], y: +b[1] } : m - 2 === l && (n[2] = { x: +b[0], y: +b[1] }, n[3] = { x: +b[2], y: +b[3] }) : n[0] = { x: +b[m - 2], y: +b[m - 1] } : m - 4 === l ? n[3] = n[2] : l || (n[0] = { x: +b[l], y: +b[l + 1] }), k.curve(d * (-n[0].x + 6 * n[1].x + n[2].x) / 6 + e * n[2].x, d * (-n[0].y + 6 * n[1].y + n[2].y) / 6 + e * n[2].y, d * (n[1].x + 6 * n[2].x - n[3].x) / 6 + e * n[2].x, d * (n[1].y + 6 * n[2].y - n[3].y) / 6 + e * n[2].y, n[2].x, n[2].y, !1, g[(l + 2) / 2]) } return k } return c.Interpolation.none()([]) } }, c.Interpolation.monotoneCubic = function (a) { var b = { fillHoles: !1 }; return a = c.extend({}, b, a), function d(b, e) { var f = c.splitIntoSegments(b, e, { fillHoles: a.fillHoles, increasingX: !0 }); if (f.length) { if (f.length > 1) { var g = []; return f.forEach(function (a) { g.push(d(a.pathCoordinates, a.valueData)) }), c.Svg.Path.join(g) } if (b = f[0].pathCoordinates, e = f[0].valueData, b.length <= 4) return c.Interpolation.none()(b, e); var h, i, j = [], k = [], l = b.length / 2, m = [], n = [], o = [], p = []; for (h = 0; h < l; h++)j[h] = b[2 * h], k[h] = b[2 * h + 1]; for (h = 0; h < l - 1; h++)o[h] = k[h + 1] - k[h], p[h] = j[h + 1] - j[h], n[h] = o[h] / p[h]; for (m[0] = n[0], m[l - 1] = n[l - 2], h = 1; h < l - 1; h++)0 === n[h] || 0 === n[h - 1] || n[h - 1] > 0 != n[h] > 0 ? m[h] = 0 : (m[h] = 3 * (p[h - 1] + p[h]) / ((2 * p[h] + p[h - 1]) / n[h - 1] + (p[h] + 2 * p[h - 1]) / n[h]), isFinite(m[h]) || (m[h] = 0)); for (i = (new c.Svg.Path).move(j[0], k[0], !1, e[0]), h = 0; h < l - 1; h++)i.curve(j[h] + p[h] / 3, k[h] + m[h] * p[h] / 3, j[h + 1] - p[h] / 3, k[h + 1] - m[h + 1] * p[h] / 3, j[h + 1], k[h + 1], !1, e[h + 1]); return i } return c.Interpolation.none()([]) } }, c.Interpolation.step = function (a) { var b = { postpone: !0, fillHoles: !1 }; return a = c.extend({}, b, a), function (b, d) { for (var e, f, g, h = new c.Svg.Path, i = 0; i < b.length; i += 2) { var j = b[i], k = b[i + 1], l = d[i / 2]; void 0 !== l.value ? (void 0 === g ? h.move(j, k, !1, l) : (a.postpone ? h.line(j, f, !1, g) : h.line(e, k, !1, l), h.line(j, k, !1, l)), e = j, f = k, g = l) : a.fillHoles || (e = f = g = void 0) } return h } } }(window, document, a), function (a, b, c) { "use strict"; c.EventEmitter = function () { function a(a, b) { d[a] = d[a] || [], d[a].push(b) } function b(a, b) { d[a] && (b ? (d[a].splice(d[a].indexOf(b), 1), 0 === d[a].length && delete d[a]) : delete d[a]) } function c(a, b) { d[a] && d[a].forEach(function (a) { a(b) }), d["*"] && d["*"].forEach(function (c) { c(a, b) }) } var d = []; return { addEventHandler: a, removeEventHandler: b, emit: c } } }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = []; if (a.length) for (var c = 0; c < a.length; c++)b.push(a[c]); return b } function e(a, b) { var d = b || this.prototype || c.Class, e = Object.create(d); c.Class.cloneDefinitions(e, a); var f = function () { var a, b = e.constructor || function () { }; return a = this === c ? Object.create(e) : this, b.apply(a, Array.prototype.slice.call(arguments, 0)), a }; return f.prototype = e, f["super"] = d, f.extend = this.extend, f } function f() { var a = d(arguments), b = a[0]; return a.splice(1, a.length - 1).forEach(function (a) { Object.getOwnPropertyNames(a).forEach(function (c) { delete b[c], Object.defineProperty(b, c, Object.getOwnPropertyDescriptor(a, c)) }) }), b } c.Class = { extend: e, cloneDefinitions: f } }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d) { return a && (this.data = a || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.eventEmitter.emit("data", { type: "update", data: this.data })), b && (this.options = c.extend({}, d ? this.options : this.defaultOptions, b), this.initializeTimeoutId || (this.optionsProvider.removeMediaQueryListeners(), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter))), this.initializeTimeoutId || this.createChart(this.optionsProvider.getCurrentOptions()), this } function e() { return this.initializeTimeoutId ? a.clearTimeout(this.initializeTimeoutId) : (a.removeEventListener("resize", this.resizeListener), this.optionsProvider.removeMediaQueryListeners()), this } function f(a, b) { return this.eventEmitter.addEventHandler(a, b), this } function g(a, b) { return this.eventEmitter.removeEventHandler(a, b), this } function h() { a.addEventListener("resize", this.resizeListener), this.optionsProvider = c.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter), this.eventEmitter.addEventHandler("optionsChanged", function () { this.update() }.bind(this)), this.options.plugins && this.options.plugins.forEach(function (a) { a instanceof Array ? a[0](this, a[1]) : a(this) }.bind(this)), this.eventEmitter.emit("data", { type: "initial", data: this.data }), this.createChart(this.optionsProvider.getCurrentOptions()), this.initializeTimeoutId = void 0 } function i(a, b, d, e, f) { this.container = c.querySelector(a), this.data = b || {}, this.data.labels = this.data.labels || [], this.data.series = this.data.series || [], this.defaultOptions = d, this.options = e, this.responsiveOptions = f, this.eventEmitter = c.EventEmitter(), this.supportsForeignObject = c.Svg.isSupported("Extensibility"), this.supportsAnimations = c.Svg.isSupported("AnimationEventsAttribute"), this.resizeListener = function () { this.update() }.bind(this), this.container && (this.container.__chartist__ && this.container.__chartist__.detach(), this.container.__chartist__ = this), this.initializeTimeoutId = setTimeout(h.bind(this), 0) } c.Base = c.Class.extend({ constructor: i, optionsProvider: void 0, container: void 0, svg: void 0, eventEmitter: void 0, createChart: function () { throw new Error("Base chart type can't be instantiated!") }, update: d, detach: e, on: f, off: g, version: c.version, supportsForeignObject: !1 }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, d, e, f, g) { a instanceof Element ? this._node = a : (this._node = b.createElementNS(c.namespaces.svg, a), "svg" === a && this.attr({ "xmlns:ct": c.namespaces.ct })), d && this.attr(d), e && this.addClass(e), f && (g && f._node.firstChild ? f._node.insertBefore(this._node, f._node.firstChild) : f._node.appendChild(this._node)) } function e(a, b) { return "string" == typeof a ? b ? this._node.getAttributeNS(b, a) : this._node.getAttribute(a) : (Object.keys(a).forEach(function (b) { if (void 0 !== a[b]) if (b.indexOf(":") !== -1) { var d = b.split(":"); this._node.setAttributeNS(c.namespaces[d[0]], b, a[b]) } else this._node.setAttribute(b, a[b]) }.bind(this)), this) } function f(a, b, d, e) { return new c.Svg(a, b, d, this, e) } function g() { return this._node.parentNode instanceof SVGElement ? new c.Svg(this._node.parentNode) : null } function h() { for (var a = this._node; "svg" !== a.nodeName;)a = a.parentNode; return new c.Svg(a) } function i(a) { var b = this._node.querySelector(a); return b ? new c.Svg(b) : null } function j(a) { var b = this._node.querySelectorAll(a); return b.length ? new c.Svg.List(b) : null } function k() { return this._node } function l(a, d, e, f) { if ("string" == typeof a) { var g = b.createElement("div"); g.innerHTML = a, a = g.firstChild } a.setAttribute("xmlns", c.namespaces.xmlns); var h = this.elem("foreignObject", d, e, f); return h._node.appendChild(a), h } function m(a) { return this._node.appendChild(b.createTextNode(a)), this } function n() { for (; this._node.firstChild;)this._node.removeChild(this._node.firstChild); return this } function o() { return this._node.parentNode.removeChild(this._node), this.parent() } function p(a) { return this._node.parentNode.replaceChild(a._node, this._node), a } function q(a, b) { return b && this._node.firstChild ? this._node.insertBefore(a._node, this._node.firstChild) : this._node.appendChild(a._node), this } function r() { return this._node.getAttribute("class") ? this._node.getAttribute("class").trim().split(/\s+/) : [] } function s(a) { return this._node.setAttribute("class", this.classes(this._node).concat(a.trim().split(/\s+/)).filter(function (a, b, c) { return c.indexOf(a) === b }).join(" ")), this } function t(a) { var b = a.trim().split(/\s+/); return this._node.setAttribute("class", this.classes(this._node).filter(function (a) { return b.indexOf(a) === -1 }).join(" ")), this } function u() { return this._node.setAttribute("class", ""), this } function v() { return this._node.getBoundingClientRect().height } function w() { return this._node.getBoundingClientRect().width } function x(a, b, d) { return void 0 === b && (b = !0), Object.keys(a).forEach(function (e) { function f(a, b) { var f, g, h, i = {}; a.easing && (h = a.easing instanceof Array ? a.easing : c.Svg.Easing[a.easing], delete a.easing), a.begin = c.ensureUnit(a.begin, "ms"), a.dur = c.ensureUnit(a.dur, "ms"), h && (a.calcMode = "spline", a.keySplines = h.join(" "), a.keyTimes = "0;1"), b && (a.fill = "freeze", i[e] = a.from, this.attr(i), g = c.quantity(a.begin || 0).value, a.begin = "indefinite"), f = this.elem("animate", c.extend({ attributeName: e }, a)), b && setTimeout(function () { try { f._node.beginElement() } catch (b) { i[e] = a.to, this.attr(i), f.remove() } }.bind(this), g), d && f._node.addEventListener("beginEvent", function () { d.emit("animationBegin", { element: this, animate: f._node, params: a }) }.bind(this)), f._node.addEventListener("endEvent", function () { d && d.emit("animationEnd", { element: this, animate: f._node, params: a }), b && (i[e] = a.to, this.attr(i), f.remove()) }.bind(this)) } a[e] instanceof Array ? a[e].forEach(function (a) { f.bind(this)(a, !1) }.bind(this)) : f.bind(this)(a[e], b) }.bind(this)), this } function y(a) { var b = this; this.svgElements = []; for (var d = 0; d < a.length; d++)this.svgElements.push(new c.Svg(a[d])); Object.keys(c.Svg.prototype).filter(function (a) { return ["constructor", "parent", "querySelector", "querySelectorAll", "replace", "append", "classes", "height", "width"].indexOf(a) === -1 }).forEach(function (a) { b[a] = function () { var d = Array.prototype.slice.call(arguments, 0); return b.svgElements.forEach(function (b) { c.Svg.prototype[a].apply(b, d) }), b } }) } c.Svg = c.Class.extend({ constructor: d, attr: e, elem: f, parent: g, root: h, querySelector: i, querySelectorAll: j, getNode: k, foreignObject: l, text: m, empty: n, remove: o, replace: p, append: q, classes: r, addClass: s, removeClass: t, removeAllClasses: u, height: v, width: w, animate: x }), c.Svg.isSupported = function (a) { return b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#" + a, "1.1") }; var z = { easeInSine: [.47, 0, .745, .715], easeOutSine: [.39, .575, .565, 1], easeInOutSine: [.445, .05, .55, .95], easeInQuad: [.55, .085, .68, .53], easeOutQuad: [.25, .46, .45, .94], easeInOutQuad: [.455, .03, .515, .955], easeInCubic: [.55, .055, .675, .19], easeOutCubic: [.215, .61, .355, 1], easeInOutCubic: [.645, .045, .355, 1], easeInQuart: [.895, .03, .685, .22], easeOutQuart: [.165, .84, .44, 1], easeInOutQuart: [.77, 0, .175, 1], easeInQuint: [.755, .05, .855, .06], easeOutQuint: [.23, 1, .32, 1], easeInOutQuint: [.86, 0, .07, 1], easeInExpo: [.95, .05, .795, .035], easeOutExpo: [.19, 1, .22, 1], easeInOutExpo: [1, 0, 0, 1], easeInCirc: [.6, .04, .98, .335], easeOutCirc: [.075, .82, .165, 1], easeInOutCirc: [.785, .135, .15, .86], easeInBack: [.6, -.28, .735, .045], easeOutBack: [.175, .885, .32, 1.275], easeInOutBack: [.68, -.55, .265, 1.55] }; c.Svg.Easing = z, c.Svg.List = c.Class.extend({ constructor: y }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e, f, g) { var h = c.extend({ command: f ? a.toLowerCase() : a.toUpperCase() }, b, g ? { data: g } : {}); d.splice(e, 0, h) } function e(a, b) { a.forEach(function (c, d) { u[c.command.toLowerCase()].forEach(function (e, f) { b(c, e, d, f, a) }) }) } function f(a, b) { this.pathElements = [], this.pos = 0, this.close = a, this.options = c.extend({}, v, b) } function g(a) { return void 0 !== a ? (this.pos = Math.max(0, Math.min(this.pathElements.length, a)), this) : this.pos } function h(a) { return this.pathElements.splice(this.pos, a), this } function i(a, b, c, e) { return d("M", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function j(a, b, c, e) { return d("L", { x: +a, y: +b }, this.pathElements, this.pos++, c, e), this } function k(a, b, c, e, f, g, h, i) { return d("C", { x1: +a, y1: +b, x2: +c, y2: +e, x: +f, y: +g }, this.pathElements, this.pos++, h, i), this } function l(a, b, c, e, f, g, h, i, j) { return d("A", { rx: +a, ry: +b, xAr: +c, lAf: +e, sf: +f, x: +g, y: +h }, this.pathElements, this.pos++, i, j), this } function m(a) { var b = a.replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/([0-9])([A-Za-z])/g, "$1 $2").split(/[\s,]+/).reduce(function (a, b) { return b.match(/[A-Za-z]/) && a.push([]), a[a.length - 1].push(b), a }, []); "Z" === b[b.length - 1][0].toUpperCase() && b.pop(); var d = b.map(function (a) { var b = a.shift(), d = u[b.toLowerCase()]; return c.extend({ command: b }, d.reduce(function (b, c, d) { return b[c] = +a[d], b }, {})) }), e = [this.pos, 0]; return Array.prototype.push.apply(e, d), Array.prototype.splice.apply(this.pathElements, e), this.pos += d.length, this } function n() { var a = Math.pow(10, this.options.accuracy); return this.pathElements.reduce(function (b, c) { var d = u[c.command.toLowerCase()].map(function (b) { return this.options.accuracy ? Math.round(c[b] * a) / a : c[b] }.bind(this)); return b + c.command + d.join(",") }.bind(this), "") + (this.close ? "Z" : "") } function o(a, b) { return e(this.pathElements, function (c, d) { c[d] *= "x" === d[0] ? a : b }), this } function p(a, b) { return e(this.pathElements, function (c, d) { c[d] += "x" === d[0] ? a : b }), this } function q(a) { return e(this.pathElements, function (b, c, d, e, f) { var g = a(b, c, d, e, f); (g || 0 === g) && (b[c] = g) }), this } function r(a) { var b = new c.Svg.Path(a || this.close); return b.pos = this.pos, b.pathElements = this.pathElements.slice().map(function (a) { return c.extend({}, a) }), b.options = c.extend({}, this.options), b } function s(a) { var b = [new c.Svg.Path]; return this.pathElements.forEach(function (d) { d.command === a.toUpperCase() && 0 !== b[b.length - 1].pathElements.length && b.push(new c.Svg.Path), b[b.length - 1].pathElements.push(d) }), b } function t(a, b, d) { for (var e = new c.Svg.Path(b, d), f = 0; f < a.length; f++)for (var g = a[f], h = 0; h < g.pathElements.length; h++)e.pathElements.push(g.pathElements[h]); return e } var u = { m: ["x", "y"], l: ["x", "y"], c: ["x1", "y1", "x2", "y2", "x", "y"], a: ["rx", "ry", "xAr", "lAf", "sf", "x", "y"] }, v = { accuracy: 3 }; c.Svg.Path = c.Class.extend({ constructor: f, position: g, remove: h, move: i, line: j, curve: k, arc: l, scale: o, translate: p, transform: q, parse: m, stringify: n, clone: r, splitByCommand: s }), c.Svg.Path.elementDescriptions = u, c.Svg.Path.join = t }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c, d) { this.units = a, this.counterUnits = a === f.x ? f.y : f.x, this.chartRect = b, this.axisLength = b[a.rectEnd] - b[a.rectStart], this.gridOffset = b[a.rectOffset], this.ticks = c, this.options = d } function e(a, b, d, e, f) { var g = e["axis" + this.units.pos.toUpperCase()], h = this.ticks.map(this.projectValue.bind(this)), i = this.ticks.map(g.labelInterpolationFnc); h.forEach(function (j, k) { var l, m = { x: 0, y: 0 }; l = h[k + 1] ? h[k + 1] - j : Math.max(this.axisLength - j, 30), c.isFalseyButZero(i[k]) && "" !== i[k] || ("x" === this.units.pos ? (j = this.chartRect.x1 + j, m.x = e.axisX.labelOffset.x, "start" === e.axisX.position ? m.y = this.chartRect.padding.top + e.axisX.labelOffset.y + (d ? 5 : 20) : m.y = this.chartRect.y1 + e.axisX.labelOffset.y + (d ? 5 : 20)) : (j = this.chartRect.y1 - j, m.y = e.axisY.labelOffset.y - (d ? l : 0), "start" === e.axisY.position ? m.x = d ? this.chartRect.padding.left + e.axisY.labelOffset.x : this.chartRect.x1 - 10 : m.x = this.chartRect.x2 + e.axisY.labelOffset.x + 10), g.showGrid && c.createGrid(j, k, this, this.gridOffset, this.chartRect[this.counterUnits.len](), a, [e.classNames.grid, e.classNames[this.units.dir]], f), g.showLabel && c.createLabel(j, l, k, i, this, g.offset, m, b, [e.classNames.label, e.classNames[this.units.dir], "start" === g.position ? e.classNames[g.position] : e.classNames.end], d, f)) }.bind(this)) } var f = { x: { pos: "x", len: "width", dir: "horizontal", rectStart: "x1", rectEnd: "x2", rectOffset: "y2" }, y: { pos: "y", len: "height", dir: "vertical", rectStart: "y2", rectEnd: "y1", rectOffset: "x1" } }; c.Axis = c.Class.extend({ constructor: d, createGridAndLabels: e, projectValue: function (a, b, c) { throw new Error("Base axis can't be instantiated!") } }), c.Axis.units = f }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.bounds = c.getBounds(d[a.rectEnd] - d[a.rectStart], f, e.scaleMinSpace || 20, e.onlyInteger), this.range = { min: this.bounds.min, max: this.bounds.max }, c.AutoScaleAxis["super"].constructor.call(this, a, d, this.bounds.values, e) } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.bounds.min) / this.bounds.range } c.AutoScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { var f = e.highLow || c.getHighLow(b, e, a.pos); this.divisor = e.divisor || 1, this.ticks = e.ticks || c.times(this.divisor).map(function (a, b) { return f.low + (f.high - f.low) / this.divisor * b }.bind(this)), this.ticks.sort(function (a, b) { return a - b }), this.range = { min: f.low, max: f.high }, c.FixedScaleAxis["super"].constructor.call(this, a, d, this.ticks, e), this.stepLength = this.axisLength / this.divisor } function e(a) { return this.axisLength * (+c.getMultiValue(a, this.units.pos) - this.range.min) / (this.range.max - this.range.min) } c.FixedScaleAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, d, e) { c.StepAxis["super"].constructor.call(this, a, d, e.ticks, e); var f = Math.max(1, e.ticks.length - (e.stretch ? 1 : 0)); this.stepLength = this.axisLength / f } function e(a, b) { return this.stepLength * b } c.StepAxis = c.Axis.extend({ constructor: d, projectValue: e }) }(window, document, a), function (a, b, c) { "use strict"; function d(a) { var b = c.normalizeData(this.data, a.reverseData, !0); this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart); var d, e, g = this.svg.elem("g").addClass(a.classNames.gridGroup), h = this.svg.elem("g"), i = this.svg.elem("g").addClass(a.classNames.labelGroup), j = c.createChartRect(this.svg, a, f.padding); d = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, j, c.extend({}, a.axisX, { ticks: b.normalized.labels, stretch: a.fullWidth })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, j, a.axisX), e = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, j, c.extend({}, a.axisY, { high: c.isNumeric(a.high) ? a.high : a.axisY.high, low: c.isNumeric(a.low) ? a.low : a.axisY.low })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, j, a.axisY), d.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), e.createGridAndLabels(g, i, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(g, j, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (f, g) { var i = h.elem("g"); i.attr({ "ct:series-name": f.name, "ct:meta": c.serialize(f.meta) }), i.addClass([a.classNames.series, f.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var k = [], l = []; b.normalized.series[g].forEach(function (a, h) { var i = { x: j.x1 + d.projectValue(a, h, b.normalized.series[g]), y: j.y1 - e.projectValue(a, h, b.normalized.series[g]) }; k.push(i.x, i.y), l.push({ value: a, valueIndex: h, meta: c.getMetaData(f, h) }) }.bind(this)); var m = { lineSmooth: c.getSeriesOption(f, a, "lineSmooth"), showPoint: c.getSeriesOption(f, a, "showPoint"), showLine: c.getSeriesOption(f, a, "showLine"), showArea: c.getSeriesOption(f, a, "showArea"), areaBase: c.getSeriesOption(f, a, "areaBase") }, n = "function" == typeof m.lineSmooth ? m.lineSmooth : m.lineSmooth ? c.Interpolation.monotoneCubic() : c.Interpolation.none(), o = n(k, l); if (m.showPoint && o.pathElements.forEach(function (b) { var h = i.elem("line", { x1: b.x, y1: b.y, x2: b.x + .01, y2: b.y }, a.classNames.point).attr({ "ct:value": [b.data.value.x, b.data.value.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(b.data.meta) }); this.eventEmitter.emit("draw", { type: "point", value: b.data.value, index: b.data.valueIndex, meta: b.data.meta, series: f, seriesIndex: g, axisX: d, axisY: e, group: i, element: h, x: b.x, y: b.y }) }.bind(this)), m.showLine) { var p = i.elem("path", { d: o.stringify() }, a.classNames.line, !0); this.eventEmitter.emit("draw", { type: "line", values: b.normalized.series[g], path: o.clone(), chartRect: j, index: g, series: f, seriesIndex: g, seriesMeta: f.meta, axisX: d, axisY: e, group: i, element: p }) } if (m.showArea && e.range) { var q = Math.max(Math.min(m.areaBase, e.range.max), e.range.min), r = j.y1 - e.projectValue(q); o.splitByCommand("M").filter(function (a) { return a.pathElements.length > 1 }).map(function (a) { var b = a.pathElements[0], c = a.pathElements[a.pathElements.length - 1]; return a.clone(!0).position(0).remove(1).move(b.x, r).line(b.x, b.y).position(a.pathElements.length + 1).line(c.x, r) }).forEach(function (c) { var h = i.elem("path", { d: c.stringify() }, a.classNames.area, !0); this.eventEmitter.emit("draw", { type: "area", values: b.normalized.series[g], path: c.clone(), series: f, seriesIndex: g, axisX: d, axisY: e, chartRect: j, index: g, group: i, element: h }) }.bind(this)) } }.bind(this)), this.eventEmitter.emit("created", { bounds: e.bounds, chartRect: j, axisX: d, axisY: e, svg: this.svg, options: a }) } function e(a, b, d, e) { c.Line["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, type: void 0, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, showLine: !0, showPoint: !0, showArea: !1, areaBase: 0, lineSmooth: !0, showGridBackground: !1, low: void 0, high: void 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, fullWidth: !1, reverseData: !1, classNames: { chart: "ct-chart-line", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", line: "ct-line", point: "ct-point", area: "ct-area", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Line = c.Base.extend({ constructor: e, createChart: d }) }(window, document, a), function (a, b, c) { - "use strict"; function d(a) { - var b, d; a.distributeSeries ? (b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), b.normalized.series = b.normalized.series.map(function (a) { return [a] })) : b = c.normalizeData(this.data, a.reverseData, a.horizontalBars ? "x" : "y"), this.svg = c.createSvg(this.container, a.width, a.height, a.classNames.chart + (a.horizontalBars ? " " + a.classNames.horizontalBars : "")); var e = this.svg.elem("g").addClass(a.classNames.gridGroup), g = this.svg.elem("g"), h = this.svg.elem("g").addClass(a.classNames.labelGroup); if (a.stackBars && 0 !== b.normalized.series.length) { - var i = c.serialMap(b.normalized.series, function () { - return Array.prototype.slice.call(arguments).map(function (a) { return a }).reduce(function (a, b) { return { x: a.x + (b && b.x) || 0, y: a.y + (b && b.y) || 0 } }, { x: 0, y: 0 }) - }); d = c.getHighLow([i], a, a.horizontalBars ? "x" : "y") - } else d = c.getHighLow(b.normalized.series, a, a.horizontalBars ? "x" : "y"); d.high = +a.high || (0 === a.high ? 0 : d.high), d.low = +a.low || (0 === a.low ? 0 : d.low); var j, k, l, m, n, o = c.createChartRect(this.svg, a, f.padding); k = a.distributeSeries && a.stackBars ? b.normalized.labels.slice(0, 1) : b.normalized.labels, a.horizontalBars ? (j = m = void 0 === a.axisX.type ? new c.AutoScaleAxis(c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, c.extend({}, a.axisX, { highLow: d, referenceValue: 0 })), l = n = void 0 === a.axisY.type ? new c.StepAxis(c.Axis.units.y, b.normalized.series, o, { ticks: k }) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, a.axisY)) : (l = m = void 0 === a.axisX.type ? new c.StepAxis(c.Axis.units.x, b.normalized.series, o, { ticks: k }) : a.axisX.type.call(c, c.Axis.units.x, b.normalized.series, o, a.axisX), j = n = void 0 === a.axisY.type ? new c.AutoScaleAxis(c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 })) : a.axisY.type.call(c, c.Axis.units.y, b.normalized.series, o, c.extend({}, a.axisY, { highLow: d, referenceValue: 0 }))); var p = a.horizontalBars ? o.x1 + j.projectValue(0) : o.y1 - j.projectValue(0), q = []; l.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), j.createGridAndLabels(e, h, this.supportsForeignObject, a, this.eventEmitter), a.showGridBackground && c.createGridBackground(e, o, a.classNames.gridBackground, this.eventEmitter), b.raw.series.forEach(function (d, e) { var f, h, i = e - (b.raw.series.length - 1) / 2; f = a.distributeSeries && !a.stackBars ? l.axisLength / b.normalized.series.length / 2 : a.distributeSeries && a.stackBars ? l.axisLength / 2 : l.axisLength / b.normalized.series[e].length / 2, h = g.elem("g"), h.attr({ "ct:series-name": d.name, "ct:meta": c.serialize(d.meta) }), h.addClass([a.classNames.series, d.className || a.classNames.series + "-" + c.alphaNumerate(e)].join(" ")), b.normalized.series[e].forEach(function (g, k) { var r, s, t, u; if (u = a.distributeSeries && !a.stackBars ? e : a.distributeSeries && a.stackBars ? 0 : k, r = a.horizontalBars ? { x: o.x1 + j.projectValue(g && g.x ? g.x : 0, k, b.normalized.series[e]), y: o.y1 - l.projectValue(g && g.y ? g.y : 0, u, b.normalized.series[e]) } : { x: o.x1 + l.projectValue(g && g.x ? g.x : 0, u, b.normalized.series[e]), y: o.y1 - j.projectValue(g && g.y ? g.y : 0, k, b.normalized.series[e]) }, l instanceof c.StepAxis && (l.options.stretch || (r[l.units.pos] += f * (a.horizontalBars ? -1 : 1)), r[l.units.pos] += a.stackBars || a.distributeSeries ? 0 : i * a.seriesBarDistance * (a.horizontalBars ? -1 : 1)), t = q[k] || p, q[k] = t - (p - r[l.counterUnits.pos]), void 0 !== g) { var v = {}; v[l.units.pos + "1"] = r[l.units.pos], v[l.units.pos + "2"] = r[l.units.pos], !a.stackBars || "accumulate" !== a.stackMode && a.stackMode ? (v[l.counterUnits.pos + "1"] = p, v[l.counterUnits.pos + "2"] = r[l.counterUnits.pos]) : (v[l.counterUnits.pos + "1"] = t, v[l.counterUnits.pos + "2"] = q[k]), v.x1 = Math.min(Math.max(v.x1, o.x1), o.x2), v.x2 = Math.min(Math.max(v.x2, o.x1), o.x2), v.y1 = Math.min(Math.max(v.y1, o.y2), o.y1), v.y2 = Math.min(Math.max(v.y2, o.y2), o.y1); var w = c.getMetaData(d, k); s = h.elem("line", v, a.classNames.bar).attr({ "ct:value": [g.x, g.y].filter(c.isNumeric).join(","), "ct:meta": c.serialize(w) }), this.eventEmitter.emit("draw", c.extend({ type: "bar", value: g, index: k, meta: w, series: d, seriesIndex: e, axisX: m, axisY: n, chartRect: o, group: h, element: s }, v)) } }.bind(this)) }.bind(this)), this.eventEmitter.emit("created", { bounds: j.bounds, chartRect: o, axisX: m, axisY: n, svg: this.svg, options: a }) - } function e(a, b, d, e) { c.Bar["super"].constructor.call(this, a, b, f, c.extend({}, f, d), e) } var f = { axisX: { offset: 30, position: "end", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 30, onlyInteger: !1 }, axisY: { offset: 40, position: "start", labelOffset: { x: 0, y: 0 }, showLabel: !0, showGrid: !0, labelInterpolationFnc: c.noop, scaleMinSpace: 20, onlyInteger: !1 }, width: void 0, height: void 0, high: void 0, low: void 0, referenceValue: 0, chartPadding: { top: 15, right: 15, bottom: 5, left: 10 }, seriesBarDistance: 15, stackBars: !1, stackMode: "accumulate", horizontalBars: !1, distributeSeries: !1, reverseData: !1, showGridBackground: !1, classNames: { chart: "ct-chart-bar", horizontalBars: "ct-horizontal-bars", label: "ct-label", labelGroup: "ct-labels", series: "ct-series", bar: "ct-bar", grid: "ct-grid", gridGroup: "ct-grids", gridBackground: "ct-grid-background", vertical: "ct-vertical", horizontal: "ct-horizontal", start: "ct-start", end: "ct-end" } }; c.Bar = c.Base.extend({ constructor: e, createChart: d }) - }(window, document, a), function (a, b, c) { "use strict"; function d(a, b, c) { var d = b.x > a.x; return d && "explode" === c || !d && "implode" === c ? "start" : d && "implode" === c || !d && "explode" === c ? "end" : "middle" } function e(a) { var b, e, f, h, i, j = c.normalizeData(this.data), k = [], l = a.startAngle; this.svg = c.createSvg(this.container, a.width, a.height, a.donut ? a.classNames.chartDonut : a.classNames.chartPie), e = c.createChartRect(this.svg, a, g.padding), f = Math.min(e.width() / 2, e.height() / 2), i = a.total || j.normalized.series.reduce(function (a, b) { return a + b }, 0); var m = c.quantity(a.donutWidth); "%" === m.unit && (m.value *= f / 100), f -= a.donut && !a.donutSolid ? m.value / 2 : 0, h = "outside" === a.labelPosition || a.donut && !a.donutSolid ? f : "center" === a.labelPosition ? 0 : a.donutSolid ? f - m.value / 2 : f / 2, h += a.labelOffset; var n = { x: e.x1 + e.width() / 2, y: e.y2 + e.height() / 2 }, o = 1 === j.raw.series.filter(function (a) { return a.hasOwnProperty("value") ? 0 !== a.value : 0 !== a }).length; j.raw.series.forEach(function (a, b) { k[b] = this.svg.elem("g", null, null) }.bind(this)), a.showLabel && (b = this.svg.elem("g", null, null)), j.raw.series.forEach(function (e, g) { if (0 !== j.normalized.series[g] || !a.ignoreEmptyValues) { k[g].attr({ "ct:series-name": e.name }), k[g].addClass([a.classNames.series, e.className || a.classNames.series + "-" + c.alphaNumerate(g)].join(" ")); var p = i > 0 ? l + j.normalized.series[g] / i * 360 : 0, q = Math.max(0, l - (0 === g || o ? 0 : .2)); p - q >= 359.99 && (p = q + 359.99); var r, s, t, u = c.polarToCartesian(n.x, n.y, f, q), v = c.polarToCartesian(n.x, n.y, f, p), w = new c.Svg.Path(!a.donut || a.donutSolid).move(v.x, v.y).arc(f, f, 0, p - l > 180, 0, u.x, u.y); a.donut ? a.donutSolid && (t = f - m.value, r = c.polarToCartesian(n.x, n.y, t, l - (0 === g || o ? 0 : .2)), s = c.polarToCartesian(n.x, n.y, t, p), w.line(r.x, r.y), w.arc(t, t, 0, p - l > 180, 1, s.x, s.y)) : w.line(n.x, n.y); var x = a.classNames.slicePie; a.donut && (x = a.classNames.sliceDonut, a.donutSolid && (x = a.classNames.sliceDonutSolid)); var y = k[g].elem("path", { d: w.stringify() }, x); if (y.attr({ "ct:value": j.normalized.series[g], "ct:meta": c.serialize(e.meta) }), a.donut && !a.donutSolid && (y._node.style.strokeWidth = m.value + "px"), this.eventEmitter.emit("draw", { type: "slice", value: j.normalized.series[g], totalDataSum: i, index: g, meta: e.meta, series: e, group: k[g], element: y, path: w.clone(), center: n, radius: f, startAngle: l, endAngle: p }), a.showLabel) { var z; z = 1 === j.raw.series.length ? { x: n.x, y: n.y } : c.polarToCartesian(n.x, n.y, h, l + (p - l) / 2); var A; A = j.normalized.labels && !c.isFalseyButZero(j.normalized.labels[g]) ? j.normalized.labels[g] : j.normalized.series[g]; var B = a.labelInterpolationFnc(A, g); if (B || 0 === B) { var C = b.elem("text", { dx: z.x, dy: z.y, "text-anchor": d(n, z, a.labelDirection) }, a.classNames.label).text("" + B); this.eventEmitter.emit("draw", { type: "label", index: g, group: b, element: C, text: "" + B, x: z.x, y: z.y }) } } l = p } }.bind(this)), this.eventEmitter.emit("created", { chartRect: e, svg: this.svg, options: a }) } function f(a, b, d, e) { c.Pie["super"].constructor.call(this, a, b, g, c.extend({}, g, d), e) } var g = { width: void 0, height: void 0, chartPadding: 5, classNames: { chartPie: "ct-chart-pie", chartDonut: "ct-chart-donut", series: "ct-series", slicePie: "ct-slice-pie", sliceDonut: "ct-slice-donut", sliceDonutSolid: "ct-slice-donut-solid", label: "ct-label" }, startAngle: 0, total: void 0, donut: !1, donutSolid: !1, donutWidth: 60, showLabel: !0, labelOffset: 0, labelPosition: "inside", labelInterpolationFnc: c.noop, labelDirection: "neutral", reverseData: !1, ignoreEmptyValues: !1 }; c.Pie = c.Base.extend({ constructor: f, createChart: e, determineAnchorPosition: d }) }(window, document, a), a -}); - -var i, l, selectedLine = null; - -/* Navigate to hash without browser history entry */ -var navigateToHash = function () { - if (window.history !== undefined && window.history.replaceState !== undefined) { - window.history.replaceState(undefined, undefined, this.getAttribute("href")); - } -}; - -var hashLinks = document.getElementsByClassName('navigatetohash'); -for (i = 0, l = hashLinks.length; i < l; i++) { - hashLinks[i].addEventListener('click', navigateToHash); -} - -/* Switch test method */ -var switchTestMethod = function () { - var method = this.getAttribute("value"); - console.log("Selected test method: " + method); - - var lines, i, l, coverageData, lineAnalysis, cells; - - lines = document.querySelectorAll('.lineAnalysis tr'); - - for (i = 1, l = lines.length; i < l; i++) { - coverageData = JSON.parse(lines[i].getAttribute('data-coverage').replace(/'/g, '"')); - lineAnalysis = coverageData[method]; - cells = lines[i].querySelectorAll('td'); - if (lineAnalysis === undefined) { - lineAnalysis = coverageData.AllTestMethods; - if (lineAnalysis.LVS !== 'gray') { - cells[0].setAttribute('class', 'red'); - cells[1].innerText = cells[1].textContent = '0'; - cells[4].setAttribute('class', 'lightred'); - } - } else { - cells[0].setAttribute('class', lineAnalysis.LVS); - cells[1].innerText = cells[1].textContent = lineAnalysis.VC; - cells[4].setAttribute('class', 'light' + lineAnalysis.LVS); - } - } -}; - -var testMethods = document.getElementsByClassName('switchtestmethod'); -for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].addEventListener('change', switchTestMethod); -} - -/* Highlight test method by line */ -var toggleLine = function () { - if (selectedLine === this) { - selectedLine = null; - } else { - selectedLine = null; - unhighlightTestMethods(); - highlightTestMethods.call(this); - selectedLine = this; - } - -}; -var highlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var lineAnalysis; - var coverageData = JSON.parse(this.getAttribute('data-coverage').replace(/'/g, '"')); - var testMethods = document.getElementsByClassName('testmethod'); - - for (i = 0, l = testMethods.length; i < l; i++) { - lineAnalysis = coverageData[testMethods[i].id]; - if (lineAnalysis === undefined) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } else { - testMethods[i].className += ' light' + lineAnalysis.LVS; - } - } -}; -var unhighlightTestMethods = function () { - if (selectedLine !== null) { - return; - } - - var testMethods = document.getElementsByClassName('testmethod'); - for (i = 0, l = testMethods.length; i < l; i++) { - testMethods[i].className = testMethods[i].className.replace(/\s*light.+/g, ""); - } -}; -var coverableLines = document.getElementsByClassName('coverableline'); -for (i = 0, l = coverableLines.length; i < l; i++) { - coverableLines[i].addEventListener('click', toggleLine); - coverableLines[i].addEventListener('mouseenter', highlightTestMethods); - coverableLines[i].addEventListener('mouseleave', unhighlightTestMethods); -} - -/* History charts */ -var renderChart = function (chart) { - // Remove current children (e.g. PNG placeholder) - while (chart.firstChild) { - chart.firstChild.remove(); - } - - var chartData = window[chart.getAttribute('data-data')]; - var options = { - axisY: { - type: undefined, - onlyInteger: true - }, - lineSmooth: false, - low: 0, - high: 100, - scaleMinSpace: 20, - onlyInteger: true, - fullWidth: true - }; - var lineChart = new Chartist.Line(chart, { - labels: [], - series: chartData.series - }, options); - - /* Zoom */ - var zoomButtonDiv = document.createElement("div"); - zoomButtonDiv.className = "toggleZoom"; - var zoomButtonLink = document.createElement("a"); - zoomButtonLink.setAttribute("href", ""); - var zoomButtonText = document.createElement("i"); - zoomButtonText.className = "icon-search-plus"; - - zoomButtonLink.appendChild(zoomButtonText); - zoomButtonDiv.appendChild(zoomButtonLink); - - chart.appendChild(zoomButtonDiv); - - zoomButtonDiv.addEventListener('click', function (event) { - event.preventDefault(); - - if (options.axisY.type === undefined) { - options.axisY.type = Chartist.AutoScaleAxis; - zoomButtonText.className = "icon-search-minus"; - } else { - options.axisY.type = undefined; - zoomButtonText.className = "icon-search-plus"; - } - - lineChart.update(null, options); - }); - - var tooltip = document.createElement("div"); - tooltip.className = "tooltip"; - - chart.appendChild(tooltip); - - /* Tooltips */ - var showToolTip = function () { - var point = this; - var index = [].slice.call(chart.getElementsByClassName('ct-point')).indexOf(point); - - tooltip.innerHTML = chartData.tooltips[index % chartData.tooltips.length]; - tooltip.style.display = 'block'; - }; - - var moveToolTip = function (event) { - var box = chart.getBoundingClientRect(); - var left = event.pageX - box.left - window.pageXOffset; - var top = event.pageY - box.top - window.pageYOffset; - - tooltip.style.left = left - tooltip.offsetWidth / 2 - 5 + 'px'; - tooltip.style.top = top - tooltip.offsetHeight - 40 + 'px'; - }; - - var hideToolTip = function () { - tooltip.style.display = 'none'; - }; - - chart.addEventListener('mousemove', moveToolTip); - - lineChart.on('created', function () { - var chartPoints = chart.getElementsByClassName('ct-point'); - for (i = 0, l = chartPoints.length; i < l; i++) { - chartPoints[i].addEventListener('mousemove', showToolTip); - chartPoints[i].addEventListener('mouseout', hideToolTip); - } - }); -}; - -var charts = document.getElementsByClassName('historychart'); -for (i = 0, l = charts.length; i < l; i++) { - renderChart(charts[i]); -} - -var assemblies = [ - { - "name": "ClassLibrary1", - "classes": [ - { "name": "ClassLibrary1.Calculation", "rp": "ClassLibrary1_Calculation.htm", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [33.3], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 6, "cal": 9, "tl": 18, "lcq": 33.3, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, - { - "name": "UnitTestProject1", - "classes": [ - { "name": "UnitTestProject1.UnitTest1", "rp": "UnitTestProject1_UnitTest1.htm", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [90], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 9, "ucl": 1, "cal": 10, "tl": 26, "lcq": 90, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, - { - "name": "UnitTestProject2", - "classes": [ - { "name": "UnitTestProject2.UnitTest1", "rp": "UnitTestProject2_UnitTest1.htm", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [100], "bch": [], "hc": [{ "et": "2021/3/26 - 下午 04:34:47", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "lcq": 100, "cb": 0, "tb": 0, "bcq": 0 }] }, - ]}, -]; - -var historicCoverageExecutionTimes = []; - -var riskHotspotMetrics = [ -]; - -var riskHotspots = [ -]; - -var branchCoverageAvailable = true; - - -var translations = { -'top': 'Top:', -'all': 'All', -'assembly': 'Assembly', -'class': 'Class', -'method': 'Method', -'lineCoverage': 'LineCoverage', -'noGrouping': 'No grouping', -'byAssembly': 'By assembly', -'byNamespace': 'By namespace, Level:', -'all': 'All', -'collapseAll': 'Collapse all', -'expandAll': 'Expand all', -'grouping': 'Grouping:', -'filter': 'Filter:', -'name': 'Name', -'covered': 'Covered', -'uncovered': 'Uncovered', -'coverable': 'Coverable', -'total': 'Total', -'coverage': 'Line coverage', -'branchCoverage': 'Branch coverage', -'history': 'Coverage History', -'compareHistory': 'Compare with:', -'date': 'Date', -'allChanges': 'All changes', -'lineCoverageIncreaseOnly': 'Line coverage: Increase only', -'lineCoverageDecreaseOnly': 'Line coverage: Decrease only', -'branchCoverageIncreaseOnly': 'Branch coverage: Increase only', -'branchCoverageDecreaseOnly': 'Branch coverage: Decrease only' -}; - - -!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c",this._properties=n&&n.properties||{},this._zoneDelegate=new c(this,this._parent&&this._parent._zoneDelegate,n)}return n.assertZonePatched=function(){if(t.Promise!==T.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")},Object.defineProperty(n,"root",{get:function(){for(var t=n.current;t.parent;)t=t.parent;return t},enumerable:!0,configurable:!0}),Object.defineProperty(n,"current",{get:function(){return j.zone},enumerable:!0,configurable:!0}),Object.defineProperty(n,"currentTask",{get:function(){return M},enumerable:!0,configurable:!0}),n.__load_patch=function(o,i){if(T.hasOwnProperty(o))throw Error("Already loaded patch: "+o);if(!t["__Zone_disable_"+o]){var u="Zone:"+o;r(u),T[o]=i(t,n,O),e(u,u)}},Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"name",{get:function(){return this._name},enumerable:!0,configurable:!0}),n.prototype.get=function(t){var n=this.getZoneWith(t);if(n)return n._properties[t]},n.prototype.getZoneWith=function(t){for(var n=this;n;){if(n._properties.hasOwnProperty(t))return n;n=n._parent}return null},n.prototype.fork=function(t){if(!t)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,t)},n.prototype.wrap=function(t,n){if("function"!=typeof t)throw new Error("Expecting function got: "+t);var r=this._zoneDelegate.intercept(this,t,n),e=this;return function(){return e.runGuarded(r,this,arguments,n)}},n.prototype.run=function(t,n,r,e){void 0===n&&(n=void 0),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{return this._zoneDelegate.invoke(this,t,n,r,e)}finally{j=j.parent}},n.prototype.runGuarded=function(t,n,r,e){void 0===n&&(n=null),void 0===r&&(r=null),void 0===e&&(e=null),j={parent:j,zone:this};try{try{return this._zoneDelegate.invoke(this,t,n,r,e)}catch(o){if(this._zoneDelegate.handleError(this,o))throw o}}finally{j=j.parent}},n.prototype.runTask=function(t,n,r){if(t.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");if(t.state!==g||t.type!==F){var e=t.state!=_;e&&t._transitionTo(_,m),t.runCount++;var o=M;M=t,j={parent:j,zone:this};try{t.type==S&&t.data&&!t.data.isPeriodic&&(t.cancelFn=null);try{return this._zoneDelegate.invokeTask(this,t,n,r)}catch(i){if(this._zoneDelegate.handleError(this,i))throw i}}finally{t.state!==g&&t.state!==w&&(t.type==F||t.data&&t.data.isPeriodic?e&&t._transitionTo(m,_):(t.runCount=0,this._updateTaskCount(t,-1),e&&t._transitionTo(g,_,g))),j=j.parent,M=o}}},n.prototype.scheduleTask=function(t){if(t.zone&&t.zone!==this)for(var n=this;n;){if(n===t.zone)throw Error("can not reschedule task to "+this.name+" which is descendants of the original zone "+t.zone.name);n=n.parent}t._transitionTo(b,g);var r=[];t._zoneDelegates=r,t._zone=this;try{t=this._zoneDelegate.scheduleTask(this,t)}catch(e){throw t._transitionTo(w,b,g),this._zoneDelegate.handleError(this,e),e}return t._zoneDelegates===r&&this._updateTaskCount(t,1),t.state==b&&t._transitionTo(m,b),t},n.prototype.scheduleMicroTask=function(t,n,r,e){return this.scheduleTask(new a(x,t,n,r,e,null))},n.prototype.scheduleMacroTask=function(t,n,r,e,o){return this.scheduleTask(new a(S,t,n,r,e,o))},n.prototype.scheduleEventTask=function(t,n,r,e,o){return this.scheduleTask(new a(F,t,n,r,e,o))},n.prototype.cancelTask=function(t){if(t.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(t.zone||d).name+"; Execution: "+this.name+")");t._transitionTo(k,m,_);try{this._zoneDelegate.cancelTask(this,t)}catch(n){throw t._transitionTo(w,k),this._zoneDelegate.handleError(this,n),n}return this._updateTaskCount(t,-1),t._transitionTo(g,k),t.runCount=0,t},n.prototype._updateTaskCount=function(t,n){var r=t._zoneDelegates;-1==n&&(t._zoneDelegates=null);for(var e=0;e0,macroTask:r.macroTask>0,eventTask:r.eventTask>0,change:t})},t}(),a=function(){function n(r,e,o,i,u,c){this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=r,this.source=e,this.data=i,this.scheduleFn=u,this.cancelFn=c,this.callback=o;var a=this;this.invoke=r===F&&i&&i.useG?n.invokeTask:function(){return n.invokeTask.call(t,a,this,arguments)}}return n.invokeTask=function(t,n,r){t||(t=this),K++;try{return t.runCount++,t.zone.runTask(t,n,r)}finally{1==K&&y(),K--}},Object.defineProperty(n.prototype,"zone",{get:function(){return this._zone},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"state",{get:function(){return this._state},enumerable:!0,configurable:!0}),n.prototype.cancelScheduleRequest=function(){this._transitionTo(g,b)},n.prototype._transitionTo=function(t,n,r){if(this._state!==n&&this._state!==r)throw new Error(this.type+" '"+this.source+"': can not transition to '"+t+"', expecting state '"+n+"'"+(r?" or '"+r+"'":"")+", was '"+this._state+"'.");this._state=t,t==g&&(this._zoneDelegates=null)},n.prototype.toString=function(){return this.data&&void 0!==this.data.handleId?this.data.handleId:Object.prototype.toString.call(this)},n.prototype.toJSON=function(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}},n}(),f=X("setTimeout"),s=X("Promise"),l=X("then"),h=[],p=!1;function v(n){0===K&&0===h.length&&(o||t[s]&&(o=t[s].resolve(0)),o?o[l](y):t[f](y,0)),n&&h.push(n)}function y(){if(!p){for(p=!0;h.length;){var t=h;h=[];for(var n=0;n=0;r--)"function"==typeof t[r]&&(t[r]=h(t[r],n+"_"+r));return t}function k(t){return!t||!1!==t.writable&&!("function"==typeof t.get&&void 0===t.set)}var w="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,x=!("nw"in g)&&void 0!==g.process&&"[object process]"==={}.toString.call(g.process),S=!x&&!w&&!(!y||!d.HTMLElement),F=void 0!==g.process&&"[object process]"==={}.toString.call(g.process)&&!w&&!(!y||!d.HTMLElement),T={},O=function(t){if(t=t||g.event){var n=T[t.type];n||(n=T[t.type]=v("ON_PROPERTY"+t.type));var r=(this||t.target||g)[n],e=r&&r.apply(this,arguments);return null==e||e||t.preventDefault(),e}};function j(r,e,o){var i=t(r,e);if(!i&&o&&t(o,e)&&(i={enumerable:!0,configurable:!0}),i&&i.configurable){delete i.writable,delete i.value;var u=i.get,c=i.set,a=e.substr(2),f=T[a];f||(f=T[a]=v("ON_PROPERTY"+a)),i.set=function(t){var n=this;n||r!==g||(n=g),n&&(n[f]&&n.removeEventListener(a,O),c&&c.apply(n,m),"function"==typeof t?(n[f]=t,n.addEventListener(a,O,!1)):n[f]=null)},i.get=function(){var t=this;if(t||r!==g||(t=g),!t)return null;var n=t[f];if(n)return n;if(u){var o=u&&u.call(this);if(o)return i.set.call(this,o),"function"==typeof t[b]&&t.removeAttribute(e),o}return null},n(r,e,i)}}function M(t,n,r){if(n)for(var e=0;e1?new c(n,r):new c(n),l=t(s,"onmessage");return l&&!1===l.configurable?(a=e(s),f=s,[i,u,"send","close"].forEach(function(t){a[t]=function(){var n=o.call(arguments);if(t===i||t===u){var r=n.length>0?n[0]:void 0;if(r){var e=Zone.__symbol__("ON_PROPERTY"+r);s[e]=a[e]}}return s[t].apply(s,n)}})):a=s,M(a,["close","error","message","open"],f),a};var a=r.WebSocket;for(var f in c)a[f]=c[f]}(0,a)}}var lt=v("unbound");Zone.__load_patch("util",function(t,n,r){r.patchOnProperties=M,r.patchMethod=X,r.bindArguments=_}),Zone.__load_patch("timers",function(t){G(t,"set","clear","Timeout"),G(t,"set","clear","Interval"),G(t,"set","clear","Immediate")}),Zone.__load_patch("requestAnimationFrame",function(t){G(t,"request","cancel","AnimationFrame"),G(t,"mozRequest","mozCancel","AnimationFrame"),G(t,"webkitRequest","webkitCancel","AnimationFrame")}),Zone.__load_patch("blocking",function(t,n){for(var r=["alert","prompt","confirm"],e=0;e=0&&"function"==typeof r[e.cbIdx]?p(e.name,r[e.cbIdx],e,i,null):t.apply(n,r)}})}()}),Zone.__load_patch("XHR",function(t,n){!function(n){var f=XMLHttpRequest.prototype,s=f[c],l=f[a];if(!s){var h=t.XMLHttpRequestEventTarget;if(h){var v=h.prototype;s=v[c],l=v[a]}}var y="readystatechange",d="scheduled";function g(t){XMLHttpRequest[i]=!1;var n=t.data,e=n.target,u=e[o];s||(s=e[c],l=e[a]),u&&l.call(e,y,u);var f=e[o]=function(){e.readyState===e.DONE&&!n.aborted&&XMLHttpRequest[i]&&t.state===d&&t.invoke()};return s.call(e,y,f),e[r]||(e[r]=t),k.apply(e,n.args),XMLHttpRequest[i]=!0,t}function b(){}function m(t){var n=t.data;return n.aborted=!0,w.apply(n.target,n.args)}var _=X(f,"open",function(){return function(t,n){return t[e]=0==n[2],t[u]=n[1],_.apply(t,n)}}),k=X(f,"send",function(){return function(t,n){return t[e]?k.apply(t,n):p("XMLHttpRequest.send",b,{target:t,url:t[u],isPeriodic:!1,delay:null,args:n,aborted:!1},g,m)}}),w=X(f,"abort",function(){return function(t){var n=t[r];if(n&&"string"==typeof n.type){if(null==n.cancelFn||n.data&&n.data.aborted)return;n.zone.cancelTask(n)}}})}();var r=v("xhrTask"),e=v("xhrSync"),o=v("xhrListener"),i=v("xhrScheduled"),u=v("xhrURL")}),Zone.__load_patch("geolocation",function(n){n.navigator&&n.navigator.geolocation&&function(n,r){for(var e=n.constructor.name,o=function(o){var i=r[o],u=n[i];if(u){if(!k(t(n,i)))return"continue";n[i]=function(t){var n=function(){return t.apply(this,_(arguments,e+"."+i))};return Z(n,t),n}(u)}},i=0;if;)a.call(t,u=c[f++])&&n.push(u);return n}},"1TsA":function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},"1sa7":function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},"25dN":function(t,n,r){var e=r("XKFU");e(e.S,"Object",{is:r("g6HL")})},"2OiF":function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"2Spj":function(t,n,r){var e=r("XKFU");e(e.P,"Function",{bind:r("8MEG")})},"2atp":function(t,n,r){var e=r("XKFU"),o=Math.atanh;e(e.S+e.F*!(o&&1/o(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},"3Lyj":function(t,n,r){var e=r("KroJ");t.exports=function(t,n,r){for(var o in n)e(t,o,n[o],r);return t}},"4A4+":function(t,n,r){r("2Spj"),r("f3/d"),r("IXt9"),t.exports=r("g3g5").Function},"4LiD":function(t,n,r){"use strict";var e=r("dyZX"),o=r("XKFU"),i=r("KroJ"),u=r("3Lyj"),c=r("Z6vF"),a=r("SlkY"),f=r("9gX7"),s=r("0/R4"),l=r("eeVq"),h=r("XMVh"),p=r("fyDq"),v=r("Xbzi");t.exports=function(t,n,r,y,d,g){var b=e[t],m=b,_=d?"set":"add",k=m&&m.prototype,w={},x=function(t){var n=k[t];i(k,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||k.forEach&&!l(function(){(new m).entries().next()}))){var S=new m,F=S[_](g?{}:-0,1)!=S,T=l(function(){S.has(1)}),O=h(function(t){new m(t)}),j=!g&&l(function(){for(var t=new m,n=5;n--;)t[_](n,n);return!t.has(-0)});O||((m=n(function(n,r){f(n,m,t);var e=v(new b,n,m);return null!=r&&a(r,d,e[_],e),e})).prototype=k,k.constructor=m),(T||j)&&(x("delete"),x("has"),d&&x("get")),(j||F)&&x(_),g&&k.clear&&delete k.clear}else m=y.getConstructor(n,t,d,_),u(m.prototype,r),c.NEED=!0;return p(m,t),w[t]=m,o(o.G+o.W+o.F*(m!=b),w),g||y.setStrong(m,t,d),m}},"4R4u":function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"5Pf0":function(t,n,r){var e=r("S/j/"),o=r("OP3Y");r("Xtr8")("getPrototypeOf",function(){return function(t){return o(e(t))}})},"69bn":function(t,n,r){var e=r("y3w9"),o=r("2OiF"),i=r("K0xU")("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||null==(r=e(u)[i])?n:o(r)}},"6AQ9":function(t,n,r){"use strict";var e=r("XKFU"),o=r("8a7r");e(e.S+e.F*r("eeVq")(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)o(r,t,arguments[t++]);return r.length=n,r}})},"6FMO":function(t,n,r){var e=r("0/R4"),o=r("EWmC"),i=r("K0xU")("species");t.exports=function(t){var n;return o(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!o(n.prototype)||(n=void 0),e(n)&&null===(n=n[i])&&(n=void 0)),void 0===n?Array:n}},"7h0T":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isNaN:function(t){return t!=t}})},"8+KV":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(0),i=r("LyE8")([].forEach,!0);e(e.P+e.F*!i,"Array",{forEach:function(t){return o(this,t,arguments[1])}})},"84bF":function(t,n,r){"use strict";r("OGtf")("small",function(t){return function(){return t(this,"small","","")}})},"8MEG":function(t,n,r){"use strict";var e=r("2OiF"),o=r("0/R4"),i=r("MfQN"),u=[].slice,c={};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),a=function(){var e=r.concat(u.call(arguments));return this instanceof a?function(t,n,r){if(!(n in c)){for(var e=[],o=0;o0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(o(this,"Map"),t);return n&&n.v},set:function(t,n){return e.def(o(this,"Map"),0===t?0:t,n)}},e,!0)},"9P93":function(t,n,r){var e=r("XKFU"),o=Math.imul;e(e.S+e.F*r("eeVq")(function(){return-5!=o(4294967295,5)||2!=o.length}),"Math",{imul:function(t,n){var r=+t,e=+n,o=65535&r,i=65535&e;return 0|o*i+((65535&r>>>16)*i+o*(65535&e>>>16)<<16>>>0)}})},"9VmF":function(t,n,r){"use strict";var e=r("XKFU"),o=r("ne8i"),i=r("0sh+"),u="".startsWith;e(e.P+e.F*r("UUeW")("startsWith"),"String",{startsWith:function(t){var n=i(this,t,"startsWith"),r=o(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return u?u.call(n,e,r):n.slice(r,r+e.length)===e}})},"9gX7":function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},A2zW:function(t,n,r){"use strict";var e=r("XKFU"),o=r("RYi7"),i=r("vvmO"),u=r("l0Rn"),c=1..toFixed,a=Math.floor,f=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l=function(t,n){for(var r=-1,e=n;++r<6;)f[r]=(e+=t*f[r])%1e7,e=a(e/1e7)},h=function(t){for(var n=6,r=0;--n>=0;)f[n]=a((r+=f[n])/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==f[t]){var r=String(f[t]);n=""===n?r:n+u.call("0",7-r.length)+r}return n},v=function(t,n,r){return 0===n?r:n%2==1?v(t,n-1,r*t):v(t*t,n/2,r)};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r("eeVq")(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,a=i(this,s),f=o(t),y="",d="0";if(f<0||f>20)throw RangeError(s);if(a!=a)return"NaN";if(a<=-1e21||a>=1e21)return String(a);if(a<0&&(y="-",a=-a),a>1e-21)if(r=(n=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n}(a*v(2,69,1))-69)<0?a*v(2,-n,1):a/v(2,n,1),r*=4503599627370496,(n=52-n)>0){for(l(0,r),e=f;e>=7;)l(1e7,0),e-=7;for(l(v(10,e,1),0),e=n-1;e>=23;)h(1<<23),e-=23;h(1<0?y+((c=d.length)<=f?"0."+u.call("0",f-c)+d:d.slice(0,c-f)+"."+d.slice(c-f)):y+d}})},A5AN:function(t,n,r){"use strict";var e=r("AvRE")(!0);t.exports=function(t,n,r){return n+(r?e(t,n).length:1)}},Afnz:function(t,n,r){"use strict";var e=r("LQAc"),o=r("XKFU"),i=r("KroJ"),u=r("Mukb"),c=r("hPIQ"),a=r("QaDb"),f=r("fyDq"),s=r("OP3Y"),l=r("K0xU")("iterator"),h=!([].keys&&"next"in[].keys()),p=function(){return this};t.exports=function(t,n,r,v,y,d,g){a(r,n,v);var b,m,_,k=function(t){if(!h&&t in F)return F[t];switch(t){case"keys":case"values":return function(){return new r(this,t)}}return function(){return new r(this,t)}},w=n+" Iterator",x="values"==y,S=!1,F=t.prototype,T=F[l]||F["@@iterator"]||y&&F[y],O=T||k(y),j=y?x?k("entries"):O:void 0,M="Array"==n&&F.entries||T;if(M&&(_=s(M.call(new t)))!==Object.prototype&&_.next&&(f(_,w,!0),e||"function"==typeof _[l]||u(_,l,p)),x&&T&&"values"!==T.name&&(S=!0,O=function(){return T.call(this)}),e&&!g||!h&&!S&&F[l]||u(F,l,O),c[n]=O,c[w]=p,y)if(b={values:x?O:k("values"),keys:d?O:k("keys"),entries:j},g)for(m in b)m in F||i(F,m,b[m]);else o(o.P+o.F*(h||S),n,b);return b}},AphP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("S/j/"),i=r("apmT");e(e.P+e.F*r("eeVq")(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=o(this),r=i(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},AvRE:function(t,n,r){var e=r("RYi7"),o=r("vhPU");t.exports=function(t){return function(n,r){var i,u,c=String(o(n)),a=e(r),f=c.length;return a<0||a>=f?t?"":void 0:(i=c.charCodeAt(a))<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536}}},BC7C:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{fround:r("kcoS")})},"BJ/l":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log1p:r("1sa7")})},BP8U:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.S+e.F*(Number.parseInt!=o),"Number",{parseInt:o})},Btvt:function(t,n,r){"use strict";var e=r("I8a+"),o={};o[r("K0xU")("toStringTag")]="z",o+""!="[object z]"&&r("KroJ")(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},"C/va":function(t,n,r){"use strict";var e=r("y3w9");t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},CkkT:function(t,n,r){var e=r("m0Pp"),o=r("Ymqv"),i=r("S/j/"),u=r("ne8i"),c=r("zRwo");t.exports=function(t,n){var r=1==t,a=2==t,f=3==t,s=4==t,l=6==t,h=5==t||l,p=n||c;return function(n,c,v){for(var y,d,g=i(n),b=o(g),m=e(c,v,3),_=u(b.length),k=0,w=r?p(n,_):a?p(n,0):void 0;_>k;k++)if((h||k in b)&&(d=m(y=b[k],k,g),t))if(r)w[k]=d;else if(d)switch(t){case 3:return!0;case 5:return y;case 6:return k;case 2:w.push(y)}else if(s)return!1;return l?-1:f||s?s:w}}},CuTL:function(t,n,r){r("fyVe"),r("U2t9"),r("2atp"),r("+auO"),r("MtdB"),r("Jcmo"),r("nzyx"),r("BC7C"),r("x8ZO"),r("9P93"),r("eHKK"),r("BJ/l"),r("pp/T"),r("CyHz"),r("bBoP"),r("x8Yj"),r("hLT2"),t.exports=r("g3g5").Math},CyHz:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{sign:r("lvtm")})},DNiP:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduce,!0),"Array",{reduce:function(t){return o(this,t,arguments.length,arguments[1],!1)}})},DVgA:function(t,n,r){var e=r("zhAb"),o=r("4R4u");t.exports=Object.keys||function(t){return e(t,o)}},DW2E:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("freeze",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},EK0E:function(t,n,r){"use strict";var e,o=r("CkkT")(0),i=r("KroJ"),u=r("Z6vF"),c=r("czNK"),a=r("ZD67"),f=r("0/R4"),s=r("eeVq"),l=r("s5qY"),h=u.getWeak,p=Object.isExtensible,v=a.ufstore,y={},d=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},g={get:function(t){if(f(t)){var n=h(t);return!0===n?v(l(this,"WeakMap")).get(t):n?n[this._i]:void 0}},set:function(t,n){return a.def(l(this,"WeakMap"),t,n)}},b=t.exports=r("4LiD")("WeakMap",d,g,a,!0,!0);s(function(){return 7!=(new b).set((Object.freeze||Object)(y),7).get(y)})&&(c((e=a.getConstructor(d,"WeakMap")).prototype,g),u.NEED=!0,o(["delete","has","get","set"],function(t){var n=b.prototype,r=n[t];i(n,t,function(n,o){if(f(n)&&!p(n)){this._f||(this._f=new e);var i=this._f[t](n,o);return"set"==t?this:i}return r.call(this,n,o)})}))},EWmC:function(t,n,r){var e=r("LZWt");t.exports=Array.isArray||function(t){return"Array"==e(t)}},EemH:function(t,n,r){var e=r("UqcF"),o=r("RjD/"),i=r("aCFj"),u=r("apmT"),c=r("aagx"),a=r("xpql"),f=Object.getOwnPropertyDescriptor;n.f=r("nh4g")?f:function(t,n){if(t=i(t),n=u(n,!0),a)try{return f(t,n)}catch(r){}if(c(t,n))return o(!e.f.call(t,n),t[n])}},FEjr:function(t,n,r){"use strict";r("OGtf")("strike",function(t){return function(){return t(this,"strike","","")}})},FJW5:function(t,n,r){var e=r("hswa"),o=r("y3w9"),i=r("DVgA");t.exports=r("nh4g")?Object.defineProperties:function(t,n){o(t);for(var r,u=i(n),c=u.length,a=0;c>a;)e.f(t,r=u[a++],n[r]);return t}},FLlr:function(t,n,r){var e=r("XKFU");e(e.P,"String",{repeat:r("l0Rn")})},FlsD:function(t,n,r){var e=r("0/R4");r("Xtr8")("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},GNAe:function(t,n,r){var e=r("XKFU"),o=r("PKUr");e(e.G+e.F*(parseInt!=o),{parseInt:o})},H6hf:function(t,n,r){var e=r("y3w9");t.exports=function(t,n,r,o){try{return o?n(e(r)[0],r[1]):n(r)}catch(u){var i=t.return;throw void 0!==i&&e(i.call(t)),u}}},"HAE/":function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperty:r("hswa").f})},HEwt:function(t,n,r){"use strict";var e=r("m0Pp"),o=r("XKFU"),i=r("S/j/"),u=r("H6hf"),c=r("M6Qj"),a=r("ne8i"),f=r("8a7r"),s=r("J+6e");o(o.S+o.F*!r("XMVh")(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,o,l,h=i(t),p="function"==typeof this?this:Array,v=arguments.length,y=v>1?arguments[1]:void 0,d=void 0!==y,g=0,b=s(h);if(d&&(y=e(y,v>2?arguments[2]:void 0,2)),null==b||p==Array&&c(b))for(r=new p(n=a(h.length));n>g;g++)f(r,g,d?y(h[g],g):h[g]);else for(l=b.call(h),r=new p;!(o=l.next()).done;g++)f(r,g,d?u(l,y,[o.value,g],!0):o.value);return r.length=g,r}})},I78e:function(t,n,r){"use strict";var e=r("XKFU"),o=r("+rLv"),i=r("LZWt"),u=r("d/Gc"),c=r("ne8i"),a=[].slice;e(e.P+e.F*r("eeVq")(function(){o&&a.call(o)}),"Array",{slice:function(t,n){var r=c(this.length),e=i(this);if(n=void 0===n?r:n,"Array"==e)return a.call(this,t,n);for(var o=u(t,r),f=u(n,r),s=c(f-o),l=new Array(s),h=0;h1?arguments[1]:void 0)}}),r("nGyu")(i)},"IU+Z":function(t,n,r){"use strict";r("sMXx");var e=r("KroJ"),o=r("Mukb"),i=r("eeVq"),u=r("vhPU"),c=r("K0xU"),a=r("Ugos"),f=c("species"),s=!i(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")}),l=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var r="ab".split(t);return 2===r.length&&"a"===r[0]&&"b"===r[1]}();t.exports=function(t,n,r){var h=c(t),p=!i(function(){var n={};return n[h]=function(){return 7},7!=""[t](n)}),v=p?!i(function(){var n=!1,r=/a/;return r.exec=function(){return n=!0,null},"split"===t&&(r.constructor={},r.constructor[f]=function(){return r}),r[h](""),!n}):void 0;if(!p||!v||"replace"===t&&!s||"split"===t&&!l){var y=/./[h],d=r(u,h,""[t],function(t,n,r,e,o){return n.exec===a?p&&!o?{done:!0,value:y.call(n,r,e)}:{done:!0,value:t.call(r,n,e)}:{done:!1}}),g=d[1];e(String.prototype,t,d[0]),o(RegExp.prototype,h,2==n?function(t,n){return g.call(t,this,n)}:function(t){return g.call(t,this)})}}},IXt9:function(t,n,r){"use strict";var e=r("0/R4"),o=r("OP3Y"),i=r("K0xU")("hasInstance"),u=Function.prototype;i in u||r("hswa").f(u,i,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=o(t);)if(this.prototype===t)return!0;return!1}})},Iw71:function(t,n,r){var e=r("0/R4"),o=r("dyZX").document,i=e(o)&&e(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"J+6e":function(t,n,r){var e=r("I8a+"),o=r("K0xU")("iterator"),i=r("hPIQ");t.exports=r("g3g5").getIteratorMethod=function(t){if(null!=t)return t[o]||t["@@iterator"]||i[e(t)]}},JCqj:function(t,n,r){"use strict";r("OGtf")("sup",function(t){return function(){return t(this,"sup","","")}})},Jcmo:function(t,n,r){var e=r("XKFU"),o=Math.exp;e(e.S,"Math",{cosh:function(t){return(o(t=+t)+o(-t))/2}})},JduL:function(t,n,r){r("Xtr8")("getOwnPropertyNames",function(){return r("e7yV").f})},JiEa:function(t,n){n.f=Object.getOwnPropertySymbols},K0xU:function(t,n,r){var e=r("VTer")("wks"),o=r("ylqs"),i=r("dyZX").Symbol,u="function"==typeof i;(t.exports=function(t){return e[t]||(e[t]=u&&i[t]||(u?i:o)("Symbol."+t))}).store=e},KKXr:function(t,n,r){"use strict";var e=r("quPj"),o=r("y3w9"),i=r("69bn"),u=r("A5AN"),c=r("ne8i"),a=r("Xxuz"),f=r("Ugos"),s=Math.min,l=[].push,h=!!function(){try{return new RegExp("x","y")}catch(t){}}();r("IU+Z")("split",2,function(t,n,r,p){var v;return v="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var o=String(this);if(void 0===t&&0===n)return[];if(!e(t))return r.call(o,t,n);for(var i,u,c,a=[],s=0,h=void 0===n?4294967295:n>>>0,p=new RegExp(t.source,(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":"")+"g");(i=f.call(p,o))&&!((u=p.lastIndex)>s&&(a.push(o.slice(s,i.index)),i.length>1&&i.index=h));)p.lastIndex===i.index&&p.lastIndex++;return s===o.length?!c&&p.test("")||a.push(""):a.push(o.slice(s)),a.length>h?a.slice(0,h):a}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:r.call(this,t,n)}:r,[function(r,e){var o=t(this),i=null==r?void 0:r[n];return void 0!==i?i.call(r,o,e):v.call(String(o),r,e)},function(t,n){var e=p(v,t,this,n,v!==r);if(e.done)return e.value;var f=o(t),l=String(this),y=i(f,RegExp),d=f.unicode,g=new y(h?f:"^(?:"+f.source+")",(f.ignoreCase?"i":"")+(f.multiline?"m":"")+(f.unicode?"u":"")+(h?"y":"g")),b=void 0===n?4294967295:n>>>0;if(0===b)return[];if(0===l.length)return null===a(g,l)?[l]:[];for(var m=0,_=0,k=[];_document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a.prototype[i[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c.prototype=e(t),r=new c,c.prototype=null,r[u]=t):r=a(),void 0===n?r:o(r,n)}},L9s1:function(t,n,r){"use strict";var e=r("XKFU"),o=r("0sh+");e(e.P+e.F*r("UUeW")("includes"),"String",{includes:function(t){return!!~o(this,t,"includes").indexOf(t,arguments.length>1?arguments[1]:void 0)}})},LK8F:function(t,n,r){var e=r("XKFU");e(e.S,"Array",{isArray:r("EWmC")})},LQAc:function(t,n){t.exports=!1},LVwc:function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},LZWt:function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},Ljet:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},Lmuc:function(t,n,r){r("xfY5"),r("A2zW"),r("VKir"),r("Ljet"),r("/KAi"),r("fN96"),r("7h0T"),r("sbF8"),r("h/M4"),r("knhD"),r("XfKG"),r("BP8U"),t.exports=r("g3g5").Number},LyE8:function(t,n,r){"use strict";var e=r("eeVq");t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},M6Qj:function(t,n,r){var e=r("hPIQ"),o=r("K0xU")("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||i[o]===t)}},MfQN:function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},MtdB:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},Mukb:function(t,n,r){var e=r("hswa"),o=r("RjD/");t.exports=r("nh4g")?function(t,n,r){return e.f(t,n,o(1,r))}:function(t,n,r){return t[n]=r,t}},N8g3:function(t,n,r){n.f=r("K0xU")},Nr18:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=function(t){for(var n=e(this),r=i(n.length),u=arguments.length,c=o(u>1?arguments[1]:void 0,r),a=u>2?arguments[2]:void 0,f=void 0===a?r:o(a,r);f>c;)n[c++]=t;return n}},Nz9U:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=[].join;e(e.P+e.F*(r("Ymqv")!=Object||!r("LyE8")(i)),"Array",{join:function(t){return i.call(o(this),void 0===t?",":t)}})},OEbY:function(t,n,r){r("nh4g")&&"g"!=/./g.flags&&r("hswa").f(RegExp.prototype,"flags",{configurable:!0,get:r("C/va")})},OG14:function(t,n,r){"use strict";var e=r("y3w9"),o=r("g6HL"),i=r("Xxuz");r("IU+Z")("search",1,function(t,n,r,u){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=u(r,t,this);if(n.done)return n.value;var c=e(t),a=String(this),f=c.lastIndex;o(f,0)||(c.lastIndex=0);var s=i(c,a);return o(c.lastIndex,f)||(c.lastIndex=f),null===s?-1:s.index}]})},OGtf:function(t,n,r){var e=r("XKFU"),o=r("eeVq"),i=r("vhPU"),u=/"/g,c=function(t,n,r,e){var o=String(i(t)),c="<"+n;return""!==r&&(c+=" "+r+'="'+String(e).replace(u,""")+'"'),c+">"+o+""};t.exports=function(t,n){var r={};r[t]=n(c),e(e.P+e.F*o(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},OP3Y:function(t,n,r){var e=r("aagx"),o=r("S/j/"),i=r("YTvA")("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=o(t),e(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},OnI7:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("LQAc"),u=r("N8g3"),c=r("hswa").f;t.exports=function(t){var n=o.Symbol||(o.Symbol=i?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},Oyvg:function(t,n,r){var e=r("dyZX"),o=r("Xbzi"),i=r("hswa").f,u=r("kJMx").f,c=r("quPj"),a=r("C/va"),f=e.RegExp,s=f,l=f.prototype,h=/a/g,p=/a/g,v=new f(h)!==h;if(r("nh4g")&&(!v||r("eeVq")(function(){return p[r("K0xU")("match")]=!1,f(h)!=h||f(p)==p||"/a/i"!=f(h,"i")}))){f=function(t,n){var r=this instanceof f,e=c(t),i=void 0===n;return!r&&e&&t.constructor===f&&i?t:o(v?new s(e&&!i?t.source:t,n):s((e=t instanceof f)?t.source:t,e&&i?a.call(t):n),r?this:l,f)};for(var y=function(t){t in f||i(f,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})},d=u(s),g=0;d.length>g;)y(d[g++]);l.constructor=f,f.prototype=l,r("KroJ")(e,"RegExp",f)}r("elZq")("RegExp")},PKUr:function(t,n,r){var e=r("dyZX").parseInt,o=r("qncB").trim,i=r("/e88"),u=/^[-+]?0[xX]/;t.exports=8!==e(i+"08")||22!==e(i+"0x16")?function(t,n){var r=o(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},QaDb:function(t,n,r){"use strict";var e=r("Kuth"),o=r("RjD/"),i=r("fyDq"),u={};r("Mukb")(u,r("K0xU")("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:o(1,r)}),i(t,n+" Iterator")}},RW0V:function(t,n,r){var e=r("S/j/"),o=r("DVgA");r("Xtr8")("keys",function(){return function(t){return o(e(t))}})},RYi7:function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},"RjD/":function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},"S/j/":function(t,n,r){var e=r("vhPU");t.exports=function(t){return Object(e(t))}},SMB2:function(t,n,r){"use strict";r("OGtf")("bold",function(t){return function(){return t(this,"b","","")}})},SPin:function(t,n,r){"use strict";var e=r("XKFU"),o=r("eyMr");e(e.P+e.F*!r("LyE8")([].reduceRight,!0),"Array",{reduceRight:function(t){return o(this,t,arguments.length,arguments[1],!0)}})},SRfc:function(t,n,r){"use strict";var e=r("y3w9"),o=r("ne8i"),i=r("A5AN"),u=r("Xxuz");r("IU+Z")("match",1,function(t,n,r,c){return[function(r){var e=t(this),o=null==r?void 0:r[n];return void 0!==o?o.call(r,e):new RegExp(r)[n](String(e))},function(t){var n=c(r,t,this);if(n.done)return n.value;var a=e(t),f=String(this);if(!a.global)return u(a,f);var s=a.unicode;a.lastIndex=0;for(var l,h=[],p=0;null!==(l=u(a,f));){var v=String(l[0]);h[p]=v,""===v&&(a.lastIndex=i(f,o(a.lastIndex),s)),p++}return 0===p?null:h}]})},SlkY:function(t,n,r){var e=r("m0Pp"),o=r("H6hf"),i=r("M6Qj"),u=r("y3w9"),c=r("ne8i"),a=r("J+6e"),f={},s={};(n=t.exports=function(t,n,r,l,h){var p,v,y,d,g=h?function(){return t}:a(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(i(g)){for(p=c(t.length);p>m;m++)if((d=n?b(u(v=t[m])[0],v[1]):b(t[m]))===f||d===s)return d}else for(y=g.call(t);!(v=y.next()).done;)if((d=o(y,b,v.value,n))===f||d===s)return d}).BREAK=f,n.RETURN=s},T39b:function(t,n,r){"use strict";var e=r("wmvG"),o=r("s5qY");t.exports=r("4LiD")("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(o(this,"Set"),t=0===t?0:t,t)}},e)},Tze0:function(t,n,r){"use strict";r("qncB")("trim",function(t){return function(){return t(this,3)}})},U2t9:function(t,n,r){var e=r("XKFU"),o=Math.asinh;e(e.S+e.F*!(o&&1/o(0)>0),"Math",{asinh:function t(n){return isFinite(n=+n)&&0!=n?n<0?-t(-n):Math.log(n+Math.sqrt(n*n+1)):n}})},UUeW:function(t,n,r){var e=r("K0xU")("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(o){}}return!0}},Ugos:function(t,n,r){"use strict";var e,o,i=r("C/va"),u=RegExp.prototype.exec,c=String.prototype.replace,a=u,f=(o=/b*/g,u.call(e=/a/,"a"),u.call(o,"a"),0!==e.lastIndex||0!==o.lastIndex),s=void 0!==/()??/.exec("")[1];(f||s)&&(a=function(t){var n,r,e,o,a=this;return s&&(r=new RegExp("^"+a.source+"$(?!\\s)",i.call(a))),f&&(n=a.lastIndex),e=u.call(a,t),f&&e&&(a.lastIndex=a.global?e.index+e[0].length:n),s&&e&&e.length>1&&c.call(e[0],r,function(){for(o=1;ou;){if(n=+arguments[u++],o(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?i(n):i(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},WLL4:function(t,n,r){var e=r("XKFU");e(e.S+e.F*!r("nh4g"),"Object",{defineProperties:r("FJW5")})},XKFU:function(t,n,r){var e=r("dyZX"),o=r("g3g5"),i=r("Mukb"),u=r("KroJ"),c=r("m0Pp"),a=function(t,n,r){var f,s,l,h,p=t&a.F,v=t&a.G,y=t&a.P,d=t&a.B,g=v?e:t&a.S?e[n]||(e[n]={}):(e[n]||{}).prototype,b=v?o:o[n]||(o[n]={}),m=b.prototype||(b.prototype={});for(f in v&&(r=n),r)l=((s=!p&&g&&void 0!==g[f])?g:r)[f],h=d&&s?c(l,e):y&&"function"==typeof l?c(Function.call,l):l,g&&u(g,f,l,t&a.U),b[f]!=l&&i(b,f,h),y&&m[f]!=l&&(m[f]=l)};e.core=o,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},XMVh:function(t,n,r){var e=r("K0xU")("iterator"),o=!1;try{var i=[7][e]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(u){}t.exports=function(t,n){if(!n&&!o)return!1;var r=!1;try{var i=[7],c=i[e]();c.next=function(){return{done:r=!0}},i[e]=function(){return c},t(i)}catch(u){}return r}},Xbzi:function(t,n,r){var e=r("0/R4"),o=r("i5dc").set;t.exports=function(t,n,r){var i,u=n.constructor;return u!==r&&"function"==typeof u&&(i=u.prototype)!==r.prototype&&e(i)&&o&&o(t,i),t}},XfKG:function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.S+e.F*(Number.parseFloat!=o),"Number",{parseFloat:o})},XfO3:function(t,n,r){"use strict";var e=r("AvRE")(!0);r("Afnz")(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},Xtr8:function(t,n,r){var e=r("XKFU"),o=r("g3g5"),i=r("eeVq");t.exports=function(t,n){var r=(o.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*i(function(){r(1)}),"Object",u)}},Xxuz:function(t,n,r){"use strict";var e=r("I8a+"),o=RegExp.prototype.exec;t.exports=function(t,n){var r=t.exec;if("function"==typeof r){var i=r.call(t,n);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==e(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},YJVH:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(4);e(e.P+e.F*!r("LyE8")([].every,!0),"Array",{every:function(t){return o(this,t,arguments[1])}})},YTvA:function(t,n,r){var e=r("VTer")("keys"),o=r("ylqs");t.exports=function(t){return e[t]||(e[t]=o(t))}},Ymqv:function(t,n,r){var e=r("LZWt");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},Z6vF:function(t,n,r){var e=r("ylqs")("meta"),o=r("0/R4"),i=r("aagx"),u=r("hswa").f,c=0,a=Object.isExtensible||function(){return!0},f=!r("eeVq")(function(){return a(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=t.exports={KEY:e,NEED:!1,fastKey:function(t,n){if(!o(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,e)){if(!a(t))return"F";if(!n)return"E";s(t)}return t[e].i},getWeak:function(t,n){if(!i(t,e)){if(!a(t))return!0;if(!n)return!1;s(t)}return t[e].w},onFreeze:function(t){return f&&l.NEED&&a(t)&&!i(t,e)&&s(t),t}}},ZD67:function(t,n,r){"use strict";var e=r("3Lyj"),o=r("Z6vF").getWeak,i=r("y3w9"),u=r("0/R4"),c=r("9gX7"),a=r("SlkY"),f=r("CkkT"),s=r("aagx"),l=r("s5qY"),h=f(5),p=f(6),v=0,y=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},g=function(t,n){return h(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=g(this,t);if(n)return n[1]},has:function(t){return!!g(this,t)},set:function(t,n){var r=g(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=p(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,i){var f=t(function(t,e){c(t,f,n,"_i"),t._t=n,t._i=v++,t._l=void 0,null!=e&&a(e,r,t[i],t)});return e(f.prototype,{delete:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).delete(t):r&&s(r,this._i)&&delete r[this._i]},has:function(t){if(!u(t))return!1;var r=o(t);return!0===r?y(l(this,n)).has(t):r&&s(r,this._i)}}),f},def:function(t,n,r){var e=o(i(n),!0);return!0===e?y(t).set(n,r):e[t._i]=r,t},ufstore:y}},Zshi:function(t,n,r){var e=r("0/R4");r("Xtr8")("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},Zz4T:function(t,n,r){"use strict";r("OGtf")("sub",function(t){return function(){return t(this,"sub","","")}})},a1Th:function(t,n,r){"use strict";r("OEbY");var e=r("y3w9"),o=r("C/va"),i=r("nh4g"),u=/./.toString,c=function(t){r("KroJ")(RegExp.prototype,"toString",t,!0)};r("eeVq")(function(){return"/a/b"!=u.call({source:"a",flags:"b"})})?c(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!i&&t instanceof RegExp?o.call(t):void 0)}):"toString"!=u.name&&c(function(){return u.call(this)})},aCFj:function(t,n,r){var e=r("Ymqv"),o=r("vhPU");t.exports=function(t){return e(o(t))}},aagx:function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},apmT:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t))return t;var r,o;if(n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;if("function"==typeof(r=t.valueOf)&&!e(o=r.call(t)))return o;if(!n&&"function"==typeof(r=t.toString)&&!e(o=r.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},bBoP:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S+e.F*r("eeVq")(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(o(t)-o(-t))/2:(i(t-1)-i(-t-1))*(Math.E/2)}})},bDcW:function(t,n,r){"use strict";r("OGtf")("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},bHtr:function(t,n,r){var e=r("XKFU");e(e.P,"Array",{fill:r("Nr18")}),r("nGyu")("fill")},bWfx:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(1);e(e.P+e.F*!r("LyE8")([].map,!0),"Array",{map:function(t){return o(this,t,arguments[1])}})},czNK:function(t,n,r){"use strict";var e=r("DVgA"),o=r("JiEa"),i=r("UqcF"),u=r("S/j/"),c=r("Ymqv"),a=Object.assign;t.exports=!a||r("eeVq")(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=a({},t)[r]||Object.keys(a({},n)).join("")!=e})?function(t,n){for(var r=u(t),a=arguments.length,f=1,s=o.f,l=i.f;a>f;)for(var h,p=c(arguments[f++]),v=s?e(p).concat(s(p)):e(p),y=v.length,d=0;y>d;)l.call(p,h=v[d++])&&(r[h]=p[h]);return r}:a},"d/Gc":function(t,n,r){var e=r("RYi7"),o=Math.max,i=Math.min;t.exports=function(t,n){return(t=e(t))<0?o(t+n,0):i(t,n)}},"dE+T":function(t,n,r){var e=r("XKFU");e(e.P,"Array",{copyWithin:r("upKx")}),r("nGyu")("copyWithin")},dQfE:function(t,n,r){r("XfO3"),r("LK8F"),r("HEwt"),r("6AQ9"),r("Nz9U"),r("I78e"),r("Vd3H"),r("8+KV"),r("bWfx"),r("0l/t"),r("dZ+Y"),r("YJVH"),r("DNiP"),r("SPin"),r("V+eJ"),r("mGWK"),r("dE+T"),r("bHtr"),r("dRSK"),r("INYr"),r("0E+W"),r("yt8O"),t.exports=r("g3g5").Array},dRSK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(5),i=!0;"find"in[]&&Array(1).find(function(){i=!1}),e(e.P+e.F*i,"Array",{find:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}}),r("nGyu")("find")},"dZ+Y":function(t,n,r){"use strict";var e=r("XKFU"),o=r("CkkT")(3);e(e.P+e.F*!r("LyE8")([].some,!0),"Array",{some:function(t){return o(this,t,arguments[1])}})},dyZX:function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},e7yV:function(t,n,r){var e=r("aCFj"),o=r("kJMx").f,i={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return u&&"[object Window]"==i.call(t)?function(t){try{return o(t)}catch(n){return u.slice()}}(t):o(e(t))}},eHKK:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log10:function(t){return Math.log(t)*Math.LOG10E}})},eI33:function(t,n,r){var e=r("XKFU"),o=r("aCFj"),i=r("ne8i");e(e.S,"String",{raw:function(t){for(var n=o(t.raw),r=i(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c=0:l>h;h+=p)h in s&&(c=n(c,s[h],h,f));return c}},"f3/d":function(t,n,r){var e=r("hswa").f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||r("nh4g")&&e(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},fN96:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{isInteger:r("nBIS")})},fyDq:function(t,n,r){var e=r("hswa").f,o=r("aagx"),i=r("K0xU")("toStringTag");t.exports=function(t,n,r){t&&!o(t=r?t:t.prototype,i)&&e(t,i,{configurable:!0,value:n})}},fyVe:function(t,n,r){var e=r("XKFU"),o=r("1sa7"),i=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:o(t-1+i(t-1)*i(t+1))}})},g3g5:function(t,n){var r=t.exports={version:"2.6.1"};"number"==typeof __e&&(__e=r)},g4EE:function(t,n,r){"use strict";var e=r("y3w9"),o=r("apmT");t.exports=function(t){if("string"!==t&&"number"!==t&&"default"!==t)throw TypeError("Incorrect hint");return o(e(this),"number"!=t)}},g6HL:function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},"h/M4":function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},h7Nl:function(t,n,r){var e=Date.prototype,o=e.toString,i=e.getTime;new Date(NaN)+""!="Invalid Date"&&r("KroJ")(e,"toString",function(){var t=i.call(this);return t==t?o.call(this):"Invalid Date"})},hEkN:function(t,n,r){"use strict";r("OGtf")("anchor",function(t){return function(n){return t(this,"a","name",n)}})},hHhE:function(t,n,r){var e=r("XKFU");e(e.S,"Object",{create:r("Kuth")})},hLT2:function(t,n,r){var e=r("XKFU");e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},"hN/g":function(t,n,r){"use strict";r.r(n),r("dQfE"),r("nx1v"),r("4A4+"),r("qKs0"),r("CuTL"),r("Lmuc"),r("99sg"),r("ifmr"),r("oka+"),r("rfyP"),r("VXxg"),r("V5/Y"),r("vqGA"),r("hYbK"),r("0TWp")},hPIQ:function(t,n){t.exports={}},hYbK:function(t,n,r){r("Btvt"),r("yt8O"),r("EK0E"),t.exports=r("g3g5").WeakMap},hswa:function(t,n,r){var e=r("y3w9"),o=r("xpql"),i=r("apmT"),u=Object.defineProperty;n.f=r("nh4g")?Object.defineProperty:function(t,n,r){if(e(t),n=i(n,!0),e(r),o)try{return u(t,n,r)}catch(c){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},i5dc:function(t,n,r){var e=r("0/R4"),o=r("y3w9"),i=function(t,n){if(o(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{(e=r("m0Pp")(Function.call,r("EemH").f(Object.prototype,"__proto__").set,2))(t,[]),n=!(t instanceof Array)}catch(o){n=!0}return function(t,r){return i(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:i}},ifmr:function(t,n,r){r("tyy+"),t.exports=r("g3g5").parseFloat},ioFf:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("nh4g"),u=r("XKFU"),c=r("KroJ"),a=r("Z6vF").KEY,f=r("eeVq"),s=r("VTer"),l=r("fyDq"),h=r("ylqs"),p=r("K0xU"),v=r("N8g3"),y=r("OnI7"),d=r("1MBn"),g=r("EWmC"),b=r("y3w9"),m=r("0/R4"),_=r("aCFj"),k=r("apmT"),w=r("RjD/"),x=r("Kuth"),S=r("e7yV"),F=r("EemH"),T=r("hswa"),O=r("DVgA"),j=F.f,M=T.f,K=S.f,U=e.Symbol,X=e.JSON,Z=X&&X.stringify,z=p("_hidden"),E=p("toPrimitive"),D={}.propertyIsEnumerable,A=s("symbol-registry"),L=s("symbols"),I=s("op-symbols"),P=Object.prototype,C="function"==typeof U,q=e.QObject,V=!q||!q.prototype||!q.prototype.findChild,R=i&&f(function(){return 7!=x(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=j(P,n);e&&delete P[n],M(t,n,r),e&&t!==P&&M(P,n,e)}:M,G=function(t){var n=L[t]=x(U.prototype);return n._k=t,n},J=C&&"symbol"==typeof U.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof U},Y=function(t,n,r){return t===P&&Y(I,n,r),b(t),n=k(n,!0),b(r),o(L,n)?(r.enumerable?(o(t,z)&&t[z][n]&&(t[z][n]=!1),r=x(r,{enumerable:w(0,!1)})):(o(t,z)||M(t,z,w(1,{})),t[z][n]=!0),R(t,n,r)):M(t,n,r)},H=function(t,n){b(t);for(var r,e=d(n=_(n)),o=0,i=e.length;i>o;)Y(t,r=e[o++],n[r]);return t},N=function(t){var n=D.call(this,t=k(t,!0));return!(this===P&&o(L,t)&&!o(I,t))&&(!(n||!o(this,t)||!o(L,t)||o(this,z)&&this[z][t])||n)},W=function(t,n){if(t=_(t),n=k(n,!0),t!==P||!o(L,n)||o(I,n)){var r=j(t,n);return!r||!o(L,n)||o(t,z)&&t[z][n]||(r.enumerable=!0),r}},B=function(t){for(var n,r=K(_(t)),e=[],i=0;r.length>i;)o(L,n=r[i++])||n==z||n==a||e.push(n);return e},Q=function(t){for(var n,r=t===P,e=K(r?I:_(t)),i=[],u=0;e.length>u;)!o(L,n=e[u++])||r&&!o(P,n)||i.push(L[n]);return i};C||(c((U=function(){if(this instanceof U)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===P&&n.call(I,r),o(this,z)&&o(this[z],t)&&(this[z][t]=!1),R(this,t,w(1,r))};return i&&V&&R(P,t,{configurable:!0,set:n}),G(t)}).prototype,"toString",function(){return this._k}),F.f=W,T.f=Y,r("kJMx").f=S.f=B,r("UqcF").f=N,r("JiEa").f=Q,i&&!r("LQAc")&&c(P,"propertyIsEnumerable",N,!0),v.f=function(t){return G(p(t))}),u(u.G+u.W+u.F*!C,{Symbol:U});for(var $="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),tt=0;$.length>tt;)p($[tt++]);for(var nt=O(p.store),rt=0;nt.length>rt;)y(nt[rt++]);u(u.S+u.F*!C,"Symbol",{for:function(t){return o(A,t+="")?A[t]:A[t]=U(t)},keyFor:function(t){if(!J(t))throw TypeError(t+" is not a symbol!");for(var n in A)if(A[n]===t)return n},useSetter:function(){V=!0},useSimple:function(){V=!1}}),u(u.S+u.F*!C,"Object",{create:function(t,n){return void 0===n?x(t):H(x(t),n)},defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:W,getOwnPropertyNames:B,getOwnPropertySymbols:Q}),X&&u(u.S+u.F*(!C||f(function(){var t=U();return"[null]"!=Z([t])||"{}"!=Z({a:t})||"{}"!=Z(Object(t))})),"JSON",{stringify:function(t){for(var n,r,e=[t],o=1;arguments.length>o;)e.push(arguments[o++]);if(r=n=e[1],(m(n)||void 0!==t)&&!J(t))return g(n)||(n=function(t,n){if("function"==typeof r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,Z.apply(X,e)}}),U.prototype[E]||r("Mukb")(U.prototype,E,U.prototype.valueOf),l(U,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},jqX0:function(t,n,r){var e=r("XKFU"),o=r("jtBr");e(e.P+e.F*(Date.prototype.toISOString!==o),"Date",{toISOString:o})},jtBr:function(t,n,r){"use strict";var e=r("eeVq"),o=Date.prototype.getTime,i=Date.prototype.toISOString,u=function(t){return t>9?t:"0"+t};t.exports=e(function(){return"0385-07-25T07:06:39.999Z"!=i.call(new Date(-5e13-1))})||!e(function(){i.call(new Date(NaN))})?function(){if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}:i},kJMx:function(t,n,r){var e=r("zhAb"),o=r("4R4u").concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,o)}},kcoS:function(t,n,r){var e=r("lvtm"),o=Math.pow,i=o(2,-52),u=o(2,-23),c=o(2,127)*(2-u),a=o(2,-126);t.exports=Math.fround||function(t){var n,r,o=Math.abs(t),f=e(t);return oc||r!=r?f*(1/0):f*r}},knhD:function(t,n,r){var e=r("XKFU");e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},l0Rn:function(t,n,r){"use strict";var e=r("RYi7"),o=r("vhPU");t.exports=function(t){var n=String(o(this)),r="",i=e(t);if(i<0||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(n+=n))1&i&&(r+=n);return r}},lvtm:function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},m0Pp:function(t,n,r){var e=r("2OiF");t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,o){return t.call(n,r,e,o)}}return function(){return t.apply(n,arguments)}}},mGWK:function(t,n,r){"use strict";var e=r("XKFU"),o=r("aCFj"),i=r("RYi7"),u=r("ne8i"),c=[].lastIndexOf,a=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(a||!r("LyE8")(c)),"Array",{lastIndexOf:function(t){if(a)return c.apply(this,arguments)||0;var n=o(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,i(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},mYba:function(t,n,r){var e=r("aCFj"),o=r("EemH").f;r("Xtr8")("getOwnPropertyDescriptor",function(){return function(t,n){return o(e(t),n)}})},mura:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("preventExtensions",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},nBIS:function(t,n,r){var e=r("0/R4"),o=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&o(t)===t}},nGyu:function(t,n,r){var e=r("K0xU")("unscopables"),o=Array.prototype;null==o[e]&&r("Mukb")(o,e,{}),t.exports=function(t){o[e][t]=!0}},nIY7:function(t,n,r){"use strict";r("OGtf")("big",function(t){return function(){return t(this,"big","","")}})},ne8i:function(t,n,r){var e=r("RYi7"),o=Math.min;t.exports=function(t){return t>0?o(e(t),9007199254740991):0}},nh4g:function(t,n,r){t.exports=!r("eeVq")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},nsiH:function(t,n,r){"use strict";r("OGtf")("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},nx1v:function(t,n,r){r("eM6i"),r("AphP"),r("jqX0"),r("h7Nl"),r("yM4b"),t.exports=Date},nzyx:function(t,n,r){var e=r("XKFU"),o=r("LVwc");e(e.S+e.F*(o!=Math.expm1),"Math",{expm1:o})},oDIu:function(t,n,r){"use strict";var e=r("XKFU"),o=r("AvRE")(!1);e(e.P,"String",{codePointAt:function(t){return o(this,t)}})},"oka+":function(t,n,r){r("GNAe"),t.exports=r("g3g5").parseInt},pIFo:function(t,n,r){"use strict";var e=r("y3w9"),o=r("S/j/"),i=r("ne8i"),u=r("RYi7"),c=r("A5AN"),a=r("Xxuz"),f=Math.max,s=Math.min,l=Math.floor,h=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g;r("IU+Z")("replace",2,function(t,n,r,v){return[function(e,o){var i=t(this),u=null==e?void 0:e[n];return void 0!==u?u.call(e,i,o):r.call(String(i),e,o)},function(t,n){var o=v(r,t,this,n);if(o.done)return o.value;var l=e(t),h=String(this),p="function"==typeof n;p||(n=String(n));var d=l.global;if(d){var g=l.unicode;l.lastIndex=0}for(var b=[];;){var m=a(l,h);if(null===m)break;if(b.push(m),!d)break;""===String(m[0])&&(l.lastIndex=c(h,i(l.lastIndex),g))}for(var _,k="",w=0,x=0;x=w&&(k+=h.slice(w,F)+K,w=F+S.length)}return k+h.slice(w)}];function y(t,n,e,i,u,c){var a=e+t.length,f=i.length,s=p;return void 0!==u&&(u=o(u),s=h),r.call(c,s,function(r,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,e);case"'":return n.slice(a);case"<":c=u[o.slice(1,-1)];break;default:var s=+o;if(0===s)return o;if(s>f){var h=l(s/10);return 0===h?o:h<=f?void 0===i[h-1]?o.charAt(1):i[h-1]+o.charAt(1):o}c=i[s-1]}return void 0===c?"":c})}})},"pp/T":function(t,n,r){var e=r("XKFU");e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},qKs0:function(t,n,r){r("Btvt"),r("XfO3"),r("rGqo"),r("9AAn"),t.exports=r("g3g5").Map},qncB:function(t,n,r){var e=r("XKFU"),o=r("vhPU"),i=r("eeVq"),u=r("/e88"),c="["+u+"]",a=RegExp("^"+c+c+"*"),f=RegExp(c+c+"*$"),s=function(t,n,r){var o={},c=i(function(){return!!u[t]()||"\u200b\x85"!="\u200b\x85"[t]()}),a=o[t]=c?n(l):u[t];r&&(o[r]=a),e(e.P+e.F*c,"String",o)},l=s.trim=function(t,n){return t=String(o(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(f,"")),t};t.exports=s},quPj:function(t,n,r){var e=r("0/R4"),o=r("LZWt"),i=r("K0xU")("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[i])?!!n:"RegExp"==o(t))}},rGqo:function(t,n,r){for(var e=r("yt8O"),o=r("DVgA"),i=r("KroJ"),u=r("dyZX"),c=r("Mukb"),a=r("hPIQ"),f=r("K0xU"),s=f("iterator"),l=f("toStringTag"),h=a.Array,p={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},v=o(p),y=0;y1?arguments[1]:void 0,e=o(n.length),c=void 0===r?e:Math.min(o(r),e),a=String(t);return u?u.call(n,a,c):n.slice(c-a.length,c)===a}})},s5qY:function(t,n,r){var e=r("0/R4");t.exports=function(t,n){if(!e(t)||t._t!==n)throw TypeError("Incompatible receiver, "+n+" required!");return t}},sMXx:function(t,n,r){"use strict";var e=r("Ugos");r("XKFU")({target:"RegExp",proto:!0,forced:e!==/./.exec},{exec:e})},sbF8:function(t,n,r){var e=r("XKFU"),o=r("nBIS"),i=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return o(t)&&i(t)<=9007199254740991}})},tUrg:function(t,n,r){"use strict";r("OGtf")("link",function(t){return function(n){return t(this,"a","href",n)}})},"tyy+":function(t,n,r){var e=r("XKFU"),o=r("11IZ");e(e.G+e.F*(parseFloat!=o),{parseFloat:o})},upKx:function(t,n,r){"use strict";var e=r("S/j/"),o=r("d/Gc"),i=r("ne8i");t.exports=[].copyWithin||function(t,n){var r=e(this),u=i(r.length),c=o(t,u),a=o(n,u),f=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===f?u:o(f,u))-a,u-c),l=1;for(a0;)a in r?r[c]=r[a]:delete r[c],c+=l,a+=l;return r}},vhPU:function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},vqGA:function(t,n,r){r("ioFf"),r("Btvt"),t.exports=r("g3g5").Symbol},vvmO:function(t,n,r){var e=r("LZWt");t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},w2a5:function(t,n,r){var e=r("aCFj"),o=r("ne8i"),i=r("d/Gc");t.exports=function(t){return function(n,r,u){var c,a=e(n),f=o(a.length),s=i(u,f);if(t&&r!=r){for(;f>s;)if((c=a[s++])!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===r)return t||s||0;return!t&&-1}}},wmvG:function(t,n,r){"use strict";var e=r("hswa").f,o=r("Kuth"),i=r("3Lyj"),u=r("m0Pp"),c=r("9gX7"),a=r("SlkY"),f=r("Afnz"),s=r("1TsA"),l=r("elZq"),h=r("nh4g"),p=r("Z6vF").fastKey,v=r("s5qY"),y=h?"_s":"size",d=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,f){var s=t(function(t,e){c(t,s,n,"_i"),t._t=n,t._i=o(null),t._f=void 0,t._l=void 0,t[y]=0,null!=e&&a(e,r,t[f],t)});return i(s.prototype,{clear:function(){for(var t=v(this,n),r=t._i,e=t._f;e;e=e.n)e.r=!0,e.p&&(e.p=e.p.n=void 0),delete r[e.i];t._f=t._l=void 0,t[y]=0},delete:function(t){var r=v(this,n),e=d(r,t);if(e){var o=e.n,i=e.p;delete r._i[e.i],e.r=!0,i&&(i.n=o),o&&(o.p=i),r._f==e&&(r._f=o),r._l==e&&(r._l=i),r[y]--}return!!e},forEach:function(t){v(this,n);for(var r,e=u(t,arguments.length>1?arguments[1]:void 0,3);r=r?r.n:this._f;)for(e(r.v,r.k,this);r&&r.r;)r=r.p},has:function(t){return!!d(v(this,n),t)}}),h&&e(s.prototype,"size",{get:function(){return v(this,n)[y]}}),s},def:function(t,n,r){var e,o,i=d(t,n);return i?i.v=r:(t._l=i={i:o=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=i),e&&(e.n=i),t[y]++,"F"!==o&&(t._i[o]=i)),t},getEntry:d,setStrong:function(t,n,r){f(t,n,function(t,r){this._t=v(t,n),this._k=r,this._l=void 0},function(){for(var t=this._k,n=this._l;n&&n.r;)n=n.p;return this._t&&(this._l=n=n?n.n:this._t._f)?s(0,"keys"==t?n.k:"values"==t?n.v:[n.k,n.v]):(this._t=void 0,s(1))},r?"entries":"values",!r,!0),l(n)}}},x8Yj:function(t,n,r){var e=r("XKFU"),o=r("LVwc"),i=Math.exp;e(e.S,"Math",{tanh:function(t){var n=o(t=+t),r=o(-t);return n==1/0?1:r==1/0?-1:(n-r)/(i(t)+i(-t))}})},x8ZO:function(t,n,r){var e=r("XKFU"),o=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,i=0,u=0,c=arguments.length,a=0;u0?(e=r/a)*e:r;return a===1/0?1/0:a*Math.sqrt(i)}})},xfY5:function(t,n,r){"use strict";var e=r("dyZX"),o=r("aagx"),i=r("LZWt"),u=r("Xbzi"),c=r("apmT"),a=r("eeVq"),f=r("kJMx").f,s=r("EemH").f,l=r("hswa").f,h=r("qncB").trim,p=e.Number,v=p,y=p.prototype,d="Number"==i(r("Kuth")(y)),g="trim"in String.prototype,b=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){var r,e,o,i=(n=g?n.trim():h(n,3)).charCodeAt(0);if(43===i||45===i){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===i){switch(n.charCodeAt(1)){case 66:case 98:e=2,o=49;break;case 79:case 111:e=8,o=55;break;default:return+n}for(var u,a=n.slice(2),f=0,s=a.length;fo)return NaN;return parseInt(a,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(d?a(function(){y.valueOf.call(r)}):"Number"!=i(r))?u(new v(b(n)),r,p):b(n)};for(var m,_=r("nh4g")?f(v):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),k=0;_.length>k;k++)o(v,m=_[k])&&!o(p,m)&&l(p,m,s(v,m));p.prototype=y,y.constructor=p,r("KroJ")(e,"Number",p)}},xpql:function(t,n,r){t.exports=!r("nh4g")&&!r("eeVq")(function(){return 7!=Object.defineProperty(r("Iw71")("div"),"a",{get:function(){return 7}}).a})},y3w9:function(t,n,r){var e=r("0/R4");t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},yM4b:function(t,n,r){var e=r("K0xU")("toPrimitive"),o=Date.prototype;e in o||r("Mukb")(o,e,r("g4EE"))},ylqs:function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},yt8O:function(t,n,r){"use strict";var e=r("nGyu"),o=r("1TsA"),i=r("hPIQ"),u=r("aCFj");t.exports=r("Afnz")(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,o(1)):o(0,"keys"==n?r:"values"==n?t[r]:[r,t[r]])},"values"),i.Arguments=i.Array,e("keys"),e("values"),e("entries")},z2o2:function(t,n,r){var e=r("0/R4"),o=r("Z6vF").onFreeze;r("Xtr8")("seal",function(t){return function(n){return t&&e(n)?t(o(n)):n}})},zRwo:function(t,n,r){var e=r("6FMO");t.exports=function(t,n){return new(e(t))(n)}},zhAb:function(t,n,r){var e=r("aagx"),o=r("aCFj"),i=r("w2a5")(!1),u=r("YTvA")("IE_PROTO");t.exports=function(t,n){var r,c=o(t),a=0,f=[];for(r in c)r!=u&&e(c,r)&&f.push(r);for(;n.length>a;)e(c,r=n[a++])&&(~i(f,r)||f.push(r));return f}}},[[1,0]]]); - -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{0:function(n,t,e){n.exports=e("zUnb")},crnd:function(n,t){function e(n){return Promise.resolve().then(function(){var t=new Error("Cannot find module '"+n+"'");throw t.code="MODULE_NOT_FOUND",t})}e.keys=function(){return[]},e.resolve=e,n.exports=e,e.id="crnd"},zUnb:function(n,t,e){"use strict";e.r(t);var r=function(n,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,t){n.__proto__=t}||function(n,t){for(var e in t)t.hasOwnProperty(e)&&(n[e]=t[e])})(n,t)};function o(n,t){function e(){this.constructor=n}r(n,t),n.prototype=null===t?Object.create(t):(e.prototype=t.prototype,new e)}var i=function(){return(i=Object.assign||function(n){for(var t,e=1,r=arguments.length;e=0;u--)(o=n[u])&&(l=(i<3?o(l):i>3?o(t,e,l):o(t,e))||l);return i>3&&l&&Object.defineProperty(t,e,l),l}function u(n,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(n,t)}function s(n){var t="function"==typeof Symbol&&n[Symbol.iterator],e=0;return t?t.call(n):{next:function(){return n&&e>=n.length&&(n=void 0),{value:n&&n[e++],done:!n}}}}function a(n,t){var e="function"==typeof Symbol&&n[Symbol.iterator];if(!e)return n;var r,o,i=e.call(n),l=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)l.push(r.value)}catch(u){o={error:u}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(o)throw o.error}}return l}function c(){for(var n=[],t=0;t0?this._next(t.shift()):0===this.active&&this.hasCompleted&&this.destination.complete()},t}(W);function rn(n){return n}function on(){return function(n){return n.lift(new ln(n))}}var ln=function(){function n(n){this.connectable=n}return n.prototype.call=function(n,t){var e=this.connectable;e._refCount++;var r=new un(n,e),o=t.subscribe(r);return r.closed||(r.connection=e.connect()),o},n}(),un=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._refCount;if(t<=0)this.connection=null;else if(n._refCount=t-1,t>1)this.connection=null;else{var e=this.connection,r=n._connection;this.connection=null,!r||e&&r!==e||r.unsubscribe()}}else this.connection=null},t}(S),sn=function(n){function t(t,e){var r=n.call(this)||this;return r.source=t,r.subjectFactory=e,r._refCount=0,r._isComplete=!1,r}return o(t,n),t.prototype._subscribe=function(n){return this.getSubject().subscribe(n)},t.prototype.getSubject=function(){var n=this._subject;return n&&!n.isStopped||(this._subject=this.subjectFactory()),this._subject},t.prototype.connect=function(){var n=this._connection;return n||(this._isComplete=!1,(n=this._connection=new _).add(this.source.subscribe(new cn(this.getSubject(),this))),n.closed?(this._connection=null,n=_.EMPTY):this._connection=n),n},t.prototype.refCount=function(){return on()(this)},t}(I).prototype,an={operator:{value:null},_refCount:{value:0,writable:!0},_subject:{value:null,writable:!0},_connection:{value:null,writable:!0},_subscribe:{value:sn._subscribe},_isComplete:{value:sn._isComplete,writable:!0},getSubject:{value:sn.getSubject},connect:{value:sn.connect},refCount:{value:sn.refCount}},cn=function(n){function t(t,e){var r=n.call(this,t)||this;return r.connectable=e,r}return o(t,n),t.prototype._error=function(t){this._unsubscribe(),n.prototype._error.call(this,t)},t.prototype._complete=function(){this.connectable._isComplete=!0,this._unsubscribe(),n.prototype._complete.call(this)},t.prototype._unsubscribe=function(){var n=this.connectable;if(n){this.connectable=null;var t=n._connection;n._refCount=0,n._subject=null,n._connection=null,t&&t.unsubscribe()}},t}(V);function fn(){return new H}function hn(n){for(var t in n)if(n[t]===hn)return t;throw Error("Could not find renamed property on target object.")}var pn=hn({ngComponentDef:hn}),dn=hn({ngInjectableDef:hn}),gn=hn({ngInjectorDef:hn}),vn=hn({ngModuleDef:hn}),yn=hn({__NG_ELEMENT_ID__:hn});function mn(n){return{providedIn:n.providedIn||null,factory:n.factory,value:void 0}}function bn(n){return n.hasOwnProperty(dn)?n[dn]:null}function _n(n){return n.hasOwnProperty(gn)?n[gn]:null}var wn=function(){function n(n,t){this._desc=n,this.ngMetadataName="InjectionToken",this.ngInjectableDef=void 0!==t?mn({providedIn:t.providedIn||"root",factory:t.factory}):void 0}return n.prototype.toString=function(){return"InjectionToken "+this._desc},n}(),Cn="__parameters__";function En(n,t,e){var r=function(n){return function(){for(var t=[],e=0;e=Yn?e:e[ot]}function Nt(n){return n[St]}function Dt(n){var t=Nt(n);return t?Array.isArray(t)?t:t.lViewData:null}function Mt(n){return 32767&n}function Vt(n,t){for(var e=n>>16,r=t;e>0;)r=r[pt],e--;return r}var Ht,Rt,jt,Lt,zt,Ft,Bt,Ut,Gt=("undefined"!=typeof requestAnimationFrame&&requestAnimationFrame||setTimeout).bind(Sn);function Zt(){return Ht}function qt(){return Rt}function $t(){return jt}function Qt(n){jt=n}function Wt(n,t){jt=n,Ut=t}function Kt(){return Lt}function Jt(n){Lt=n}function Yt(){return zt}function Xt(){return Bt}function ne(){return Ut}var te=!1;function ee(){return te}function re(n){te=n}var oe=!0;function ie(n){oe=n}function le(n,t){var e=Ut;return zt=n&&n[Xn],Bt=n&&1==(1&n[nt]),oe=n&&zt.firstTemplatePass,Ht=n&&n[ct],jt=t,Lt=!0,Ut=n,e&&(e[rt]=Ft),Ft=n&&n[rt],e}function ue(n,t){t||(te||yt(Ut,zt.viewHooks,zt.viewCheckHooks,Bt),Ut[nt]&=-6),Ut[nt]|=16,Ut[lt]=zt.bindingStartIndex,le(n,null)}var se=!1;function ae(n){var t=se;return se=n,t}var ce=255,fe=0;function he(n,t){var e=de(n,t);if(-1!==e)return e;var r=t[Xn];r.firstTemplatePass&&(n.injectorIndex=t.length,pe(r.data,n),pe(t,null),pe(r.blueprint,null));var o=ge(n,t),i=Mt(o),l=Vt(o,t),u=n.injectorIndex;if(o!==Wn)for(var s=l[Xn].data,a=0;a<8;a++)t[u+a]=l[i+a]|s[i+a];return t[u+$n]=o,u}function pe(n,t){n.push(0,0,0,0,0,0,0,0,t)}function de(n,t){return-1===n.injectorIndex||n.parent&&n.parent.injectorIndex===n.injectorIndex||null==t[n.injectorIndex+$n]?-1:n.injectorIndex}function ge(n,t){if(n.parent&&-1!==n.parent.injectorIndex)return n.parent.injectorIndex;for(var e=t[it],r=1;e&&-1===e.injectorIndex;)e=(t=t[pt])[it],r++;return e?e.injectorIndex|r<<16|(e&&3===e.type?32768:0):-1}var ve={};function ye(n,t,e,r){var o=t[Xn],i=o.data[n+qn],l=i.flags,u=i.providerIndexes,s=o.data,a=!1;(null==r&&function(n){return 4096==(4096&n.flags)}(i)&&se||null!=r&&r!=o&&(null==o.node||3===o.node.type))&&(a=!0);for(var c=65535&u,f=l>>16,h=4095&l,p=a?c:c+(u>>16);p=f&&d.type===e)return me(s,t,p,i)}return ve}function me(n,t,e,r){var o,i=t[e];if(null!=(o=i)&&"object"==typeof o&&Object.getPrototypeOf(o)==Jn){var l=i;if(l.resolving)throw new Error("Circular dep for "+At(n[e]));var u=ae(l.canSeeViewProviders);l.resolving=!0;var s=void 0;l.injectImpl&&(s=Bn(l.injectImpl));var a=$t(),c=ne();Wt(r,t);try{i=t[e]=l.factory(null,n,t,r)}finally{l.injectImpl&&Bn(s),ae(u),l.resolving=!1,Wt(a,c)}}return i}function be(n,t,e){var r=64&n,o=32&n;return!!((128&n?r?o?e[t+7]:e[t+6]:o?e[t+5]:e[t+4]:r?o?e[t+3]:e[t+2]:o?e[t+1]:e[t])&1< ");else if("object"==typeof t){var o=[];for(var i in t)if(t.hasOwnProperty(i)){var l=t[i];o.push(i+":"+("string"==typeof l?JSON.stringify(l):Dn(l)))}r="{"+o.join(", ")+"}"}return"StaticInjectorError"+(e?"("+e+")":"")+"["+r+"]: "+n.replace(ze,"\n ")}function Ze(n,t){return new Error(Ge(n,t))}var qe=function(){return function(){}}(),$e=function(){return function(){}}(),Qe="ngProjectAs";function We(n){return!!n.listen}var Ke={createRenderer:function(n,t){return document}},Je=[];function Ye(n){for(var t=n[it];t&&2===t.type;)t=(n=n[tt])[it];return n}function Xe(n,t,e,r,o){0===n?We(t)?t.insertBefore(e,r,o):e.insertBefore(r,o,!0):1===n?We(t)?t.removeChild(e,r):e.removeChild(r):2===n&&t.destroyNode(r)}function nr(n){var t=n[Xn].childIndex;return-1===t?null:n[t]}function tr(n,t){var e;return n.length>=Yn&&(e=n[it])&&2===e.type?function(t,e){if(-1===t.index){var r=n[ht];return r>-1?n[tt][r]:null}return n[tt][t.parent.index]}(e):n[tt]===t?null:n[tt]}function er(n){if(n.length>=Yn){var t=n;!function(n){var t=n[Xn].cleanup;if(null!=t){for(var e=0;e=Yn?t[Xn].childIndex>-1&&(e=nr(t)):t[Ot].length&&(e=t[Ot][0]),null==e){for(;t&&!t[et]&&t!==n;)er(t),t=tr(t,n);er(t||n),e=t&&t[et]}t=e}}(n),n[nt]|=32},n.prototype.onDestroy=function(n){var t,e;e=n,function(n){return n[ut]||(n[ut]=[])}(t=this._view).push(e),t[Xn].firstTemplatePass&&function(n){return n[Xn].cleanup||(n[Xn].cleanup=[])}(t).push(t[ut].length-1,null)},n.prototype.markForCheck=function(){!function(n){for(var t=n;t&&!(64&t[nt]);)t[nt]|=4,t=t[tt];var e,r,o;t[nt]|=4,o=0===(e=t[st]).flags,e.flags|=1,o&&e.clean==or&&(e.clean=new Promise(function(n){return r=n}),e.scheduler(function(){if(1&e.flags&&(e.flags&=-2,mr(e)),2&e.flags){e.flags&=-3;var n=e.playerHandler;n&&n.flushPlayers()}e.clean=or,r(null)}))}(this._view)},n.prototype.detach=function(){this._view[nt]&=-9},n.prototype.reattach=function(){this._view[nt]|=8},n.prototype.detectChanges=function(){var n=qt();n.begin&&n.begin(),br(this.context),n.end&&n.end()},n.prototype.checkNoChanges=function(){!function(n){re(!0);try{br(n)}finally{re(!1)}}(this.context)},n.prototype.attachToViewContainerRef=function(n){this._viewContainerRef=n},n.prototype.detachFromAppRef=function(){this._appRef=null},n.prototype.attachToAppRef=function(n){this._appRef=n},n.prototype._lookUpContext=function(){return this._context=this._view[tt][this._componentIndex]},n}());function Or(n,t,e,r,o){var i=e[Xn],l=function(n,t,e){var r=$t();n.firstTemplatePass&&(e.providersResolver&&e.providersResolver(e),function(n,t,e){var o=-(r.index-Yn),i=n.data.length-(65535&r.providerIndexes);(n.expandoInstructions||(n.expandoInstructions=[])).push(o,i,1)}(n),function(n,t,e,r){n.data.push(e);var o=new Kn(r,function(n){return null!==n.template}(e),null);n.blueprint.push(o),t.push(o),function(n,t){n.expandoInstructions.push(t.hostBindings||Ee),t.hostVars&&n.expandoInstructions.push(t.hostVars)}(n,e)}(n,t,e,e.factory));var o=me(n.data,t,t.length-1,r);return function(n,t,e,r){var o=It(t,n);Ce(e,n),o&&Ce(o,n),null!=r.attributes&&3==t.type&&function(n,t){for(var e=Zt(),r=We(e),o=0;o>16,r=e+(4095&n),o=e;o',!this.inertBodyElement.querySelector||this.inertBodyElement.querySelector("svg")?(this.inertBodyElement.innerHTML='

',this.getInertBodyElement=this.inertBodyElement.querySelector&&this.inertBodyElement.querySelector("svg img")&&function(){try{return!!window.DOMParser}catch(n){return!1}}()?this.getInertBodyElement_DOMParser:this.getInertBodyElement_InertDocument):this.getInertBodyElement=this.getInertBodyElement_XHR}return n.prototype.getInertBodyElement_XHR=function(n){n=""+n+"";try{n=encodeURI(n)}catch(r){return null}var t=new XMLHttpRequest;t.responseType="document",t.open("GET","data:text/html;charset=utf-8,"+n,!1),t.send(void 0);var e=t.response.body;return e.removeChild(e.firstChild),e},n.prototype.getInertBodyElement_DOMParser=function(n){n=""+n+"";try{var t=(new window.DOMParser).parseFromString(n,"text/html").body;return t.removeChild(t.firstChild),t}catch(e){return null}},n.prototype.getInertBodyElement_InertDocument=function(n){var t=this.inertDocument.createElement("template");return"content"in t?(t.innerHTML=n,t):(this.inertBodyElement.innerHTML=n,this.defaultDoc.documentMode&&this.stripCustomNsAttrs(this.inertBodyElement),this.inertBodyElement)},n.prototype.stripCustomNsAttrs=function(n){for(var t=n.attributes,e=t.length-1;0"),!0},n.prototype.endElement=function(n){var t=n.nodeName.toLowerCase();Oo.hasOwnProperty(t)&&!wo.hasOwnProperty(t)&&(this.buf.push(""))},n.prototype.chars=function(n){this.buf.push(No(n))},n.prototype.checkClobberedElement=function(n,t){if(t&&(n.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)===Node.DOCUMENT_POSITION_CONTAINED_BY)throw new Error("Failed to sanitize html because the element is clobbered: "+n.outerHTML);return t},n}(),Io=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Po=/([^\#-~ |!])/g;function No(n){return n.replace(/&/g,"&").replace(Io,function(n){return"&#"+(1024*(n.charCodeAt(0)-55296)+(n.charCodeAt(1)-56320)+65536)+";"}).replace(Po,function(n){return"&#"+n.charCodeAt(0)+";"}).replace(//g,">")}function Do(n){return"content"in n&&function(n){return n.nodeType===Node.ELEMENT_NODE&&"TEMPLATE"===n.nodeName}(n)?n.content:null}var Mo={provide:Zr,useFactory:function(){return new eo},deps:[]},Vo=function(n){function t(t,e){var r=n.call(this)||this;return r._bootstrapComponents=[],r.destroyCbs=[],r._bootstrapComponents=(t[vn]||null).bootstrap,r.injector=function(n,t,e){return void 0===t&&(t=null),void 0===e&&(e=null),t=t||Dr(),new Mr(n,e,t)}(t,e,[Mo,{provide:qe,useValue:r}]),r.instance=r.injector.get(t),r.componentFactoryResolver=new eo,r}return o(t,n),t.prototype.destroy=function(){this.destroyCbs.forEach(function(n){return n()}),this.destroyCbs=null},t.prototype.onDestroy=function(n){this.destroyCbs.push(n)},t}(qe);!function(n){function t(t){var e=n.call(this)||this;return e.moduleType=t,e}o(t,n),t.prototype.create=function(n){return new Vo(this.moduleType,n)}}($e);var Ho=function(n){function t(t){void 0===t&&(t=!1);var e=n.call(this)||this;return e.__isAsync=t,e}return o(t,n),t.prototype.emit=function(t){n.prototype.next.call(this,t)},t.prototype.subscribe=function(t,e,r){var o,i=function(n){return null},l=function(){return null};t&&"object"==typeof t?(o=this.__isAsync?function(n){setTimeout(function(){return t.next(n)})}:function(n){t.next(n)},t.error&&(i=this.__isAsync?function(n){setTimeout(function(){return t.error(n)})}:function(n){t.error(n)}),t.complete&&(l=this.__isAsync?function(){setTimeout(function(){return t.complete()})}:function(){t.complete()})):(o=this.__isAsync?function(n){setTimeout(function(){return t(n)})}:function(n){t(n)},e&&(i=this.__isAsync?function(n){setTimeout(function(){return e(n)})}:function(n){e(n)}),r&&(l=this.__isAsync?function(){setTimeout(function(){return r()})}:function(){r()}));var u=n.prototype.subscribe.call(this,o,i,l);return t instanceof _&&t.add(u),u},t}(H),Ro=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return jo(n,Qr)},n}(),jo=Ee,Lo=function(n){return n[n.NONE=0]="NONE",n[n.HTML=1]="HTML",n[n.STYLE=2]="STYLE",n[n.SCRIPT=3]="SCRIPT",n[n.URL=4]="URL",n[n.RESOURCE_URL=5]="RESOURCE_URL",n}({}),zo=function(){return function(){}}(),Fo=new RegExp("^([-,.\"'%_!# a-zA-Z0-9]+|(?:(?:matrix|translate|scale|rotate|skew|perspective)(?:X|Y|3d)?|(?:rgb|hsl)a?|(?:repeating-)?(?:linear|radial)-gradient|(?:calc|attr))\\([-0-9.%, #a-zA-Z]+\\))$","g"),Bo=/^url\(([^)]+)\)$/;Function,String,String;var Uo="ngDebugContext",Go="ngOriginalError",Zo="ngErrorLogger";function qo(n){return n[Uo]}function $o(n){return n[Go]}function Qo(n){for(var t=[],e=1;e0&&(o=setTimeout(function(){r._callbacks=r._callbacks.filter(function(n){return n.timeoutId!==o}),n(r._didWork,r.getPendingTasks())},t)),this._callbacks.push({doneCb:n,timeoutId:o,updateCb:e})},n.prototype.whenStable=function(n,t,e){if(e&&!this.taskTrackingZone)throw new Error('Task tracking zone is required when passing an update callback to whenStable(). Is "zone.js/dist/task-tracking.js" loaded?');this.addCallback(n,t,e),this._runCallbacksIfReady()},n.prototype.getPendingRequestCount=function(){return this._pendingCount},n.prototype.findProviders=function(n,t,e){return[]},n}(),ki=function(){function n(){this._applications=new Map,Si.addToWindow(this)}return n.prototype.registerApplication=function(n,t){this._applications.set(n,t)},n.prototype.unregisterApplication=function(n){this._applications.delete(n)},n.prototype.unregisterAllApplications=function(){this._applications.clear()},n.prototype.getTestability=function(n){return this._applications.get(n)||null},n.prototype.getAllTestabilities=function(){return Array.from(this._applications.values())},n.prototype.getAllRootElements=function(){return Array.from(this._applications.keys())},n.prototype.findTestabilityInTree=function(n,t){return void 0===t&&(t=!0),Si.findTestabilityInTree(this,n,t)},l([u("design:paramtypes",[])],n)}(),Si=new(function(){function n(){}return n.prototype.addToWindow=function(n){},n.prototype.findTestabilityInTree=function(n,t,e){return null},n}()),Ai=new wn("AllowMultipleToken"),Ti=function(){return function(n,t){this.name=n,this.token=t}}();function Ii(n,t,e){void 0===e&&(e=[]);var r="Platform: "+t,o=new wn(r);return function(t){void 0===t&&(t=[]);var i=Pi();if(!i||i.injector.get(Ai,!1))if(n)n(e.concat(t).concat({provide:o,useValue:!0}));else{var l=e.concat(t).concat({provide:o,useValue:!0});!function(n){if(Ei&&!Ei.destroyed&&!Ei.injector.get(Ai,!1))throw new Error("There can be only one platform. Destroy the previous one to create a new one.");Ei=n.get(Ni);var t=n.get(ri,null);t&&t.forEach(function(n){return n()})}(Ne.create({providers:l,name:r}))}return function(n){var t=Pi();if(!t)throw new Error("No platform exists!");if(!t.injector.get(n,null))throw new Error("A platform with a different configuration has been created. Please destroy it first.");return t}(o)}}function Pi(){return Ei&&!Ei.destroyed?Ei:null}var Ni=function(){function n(n){this._injector=n,this._modules=[],this._destroyListeners=[],this._destroyed=!1}return n.prototype.bootstrapModuleFactory=function(n,t){var e,r=this,o="noop"===(e=t?t.ngZone:void 0)?new xi:("zone.js"===e?void 0:e)||new yi({enableLongStackTrace:ho()}),i=[{provide:yi,useValue:o}];return o.run(function(){var t=Ne.create({providers:i,parent:r.injector,name:n.moduleType.name}),e=n.create(t),l=e.injector.get(Wo,null);if(!l)throw new Error("No ErrorHandler. Is platform module (BrowserModule) included?");return e.onDestroy(function(){return Vi(r._modules,e)}),o.runOutsideAngular(function(){return o.onError.subscribe({next:function(n){l.handleError(n)}})}),function(n,t,o){try{var i=((l=e.injector.get(Xo)).runInitializers(),l.donePromise.then(function(){return r._moduleDoBootstrap(e),e}));return Ko(i)?i.catch(function(e){throw t.runOutsideAngular(function(){return n.handleError(e)}),e}):i}catch(u){throw t.runOutsideAngular(function(){return n.handleError(u)}),u}var l}(l,o)})},n.prototype.bootstrapModule=function(n,t){var e=this;void 0===t&&(t=[]);var r=Di({},t);return function(n,t,e){return n.get(fi).createCompiler([t]).compileModuleAsync(e)}(this.injector,r,n).then(function(n){return e.bootstrapModuleFactory(n,r)})},n.prototype._moduleDoBootstrap=function(n){var t=n.injector.get(Mi);if(n._bootstrapComponents.length>0)n._bootstrapComponents.forEach(function(n){return t.bootstrap(n)});else{if(!n.instance.ngDoBootstrap)throw new Error("The module "+Dn(n.instance.constructor)+' was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. Please define one of these.');n.instance.ngDoBootstrap(t)}this._modules.push(n)},n.prototype.onDestroy=function(n){this._destroyListeners.push(n)},Object.defineProperty(n.prototype,"injector",{get:function(){return this._injector},enumerable:!0,configurable:!0}),n.prototype.destroy=function(){if(this._destroyed)throw new Error("The platform has already been destroyed!");this._modules.slice().forEach(function(n){return n.destroy()}),this._destroyListeners.forEach(function(n){return n()}),this._destroyed=!0},Object.defineProperty(n.prototype,"destroyed",{get:function(){return this._destroyed},enumerable:!0,configurable:!0}),n}();function Di(n,t){return Array.isArray(t)?t.reduce(Di,n):i({},n,t)}var Mi=function(){function n(n,t,e,r,o,i){var l=this;this._zone=n,this._console=t,this._injector=e,this._exceptionHandler=r,this._componentFactoryResolver=o,this._initStatus=i,this._bootstrapListeners=[],this._views=[],this._runningTick=!1,this._enforceNoNewChanges=!1,this._stable=!0,this.componentTypes=[],this.components=[],this._enforceNoNewChanges=ho(),this._zone.onMicrotaskEmpty.subscribe({next:function(){l._zone.run(function(){l.tick()})}});var u=new I(function(n){l._stable=l._zone.isStable&&!l._zone.hasPendingMacrotasks&&!l._zone.hasPendingMicrotasks,l._zone.runOutsideAngular(function(){n.next(l._stable),n.complete()})}),s=new I(function(n){var t;l._zone.runOutsideAngular(function(){t=l._zone.onStable.subscribe(function(){yi.assertNotInAngularZone(),Pn(function(){l._stable||l._zone.hasPendingMacrotasks||l._zone.hasPendingMicrotasks||(l._stable=!0,n.next(!0))})})});var e=l._zone.onUnstable.subscribe(function(){yi.assertInAngularZone(),l._stable&&(l._stable=!1,l._zone.runOutsideAngular(function(){n.next(!1)}))});return function(){t.unsubscribe(),e.unsubscribe()}});this.isStable=function(){for(var n=[],t=0;t1&&"number"==typeof n[n.length-1]&&(r=n.pop())):"number"==typeof i&&(r=n.pop()),null===o&&1===n.length&&n[0]instanceof I?n[0]:function(n){return void 0===n&&(n=Number.POSITIVE_INFINITY),function n(t,e,r){return void 0===r&&(r=Number.POSITIVE_INFINITY),"function"==typeof e?function(o){return o.pipe(n(function(n,r){return nn(t(n,r)).pipe(K(function(t,o){return e(n,t,r,o)}))},r))}:("number"==typeof e&&(r=e),function(n){return n.lift(new tn(t,r))})}(rn,n)}(r)(X(n,o))}(u,s.pipe(function(n){return on()((t=fn,function(n){var e;e="function"==typeof t?t:function(){return t};var r=Object.create(n,an);return r.source=n,r.subjectFactory=e,r})(n));var t}))}var t;return t=n,n.prototype.bootstrap=function(n,t){var e,r=this;if(!this._initStatus.done)throw new Error("Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.");e=n instanceof Fr?n:this._componentFactoryResolver.resolveComponentFactory(n),this.componentTypes.push(e.componentType);var o=e instanceof $r?null:this._injector.get(qe),i=e.create(Ne.NULL,[],t||e.selector,o);i.onDestroy(function(){r._unloadComponent(i)});var l=i.injector.get(Oi,null);return l&&i.injector.get(ki).registerApplication(i.location.nativeElement,l),this._loadComponent(i),ho()&&this._console.log("Angular is running in the development mode. Call enableProdMode() to enable the production mode."),i},n.prototype.tick=function(){var n=this;if(this._runningTick)throw new Error("ApplicationRef.tick is called recursively");var e=t._tickScope();try{this._runningTick=!0,this._views.forEach(function(n){return n.detectChanges()}),this._enforceNoNewChanges&&this._views.forEach(function(n){return n.checkNoChanges()})}catch(r){this._zone.runOutsideAngular(function(){return n._exceptionHandler.handleError(r)})}finally{this._runningTick=!1,vi(e)}},n.prototype.attachView=function(n){var t=n;this._views.push(t),t.attachToAppRef(this)},n.prototype.detachView=function(n){var t=n;Vi(this._views,t),t.detachFromAppRef()},n.prototype._loadComponent=function(n){this.attachView(n.hostView),this.tick(),this.components.push(n),this._injector.get(ii,[]).concat(this._bootstrapListeners).forEach(function(t){return t(n)})},n.prototype._unloadComponent=function(n){this.detachView(n.hostView),Vi(this.components,n)},n.prototype.ngOnDestroy=function(){this._views.slice().forEach(function(n){return n.destroy()})},Object.defineProperty(n.prototype,"viewCount",{get:function(){return this._views.length},enumerable:!0,configurable:!0}),n._tickScope=gi("ApplicationRef#tick()"),n}();function Vi(n,t){var e=n.indexOf(t);e>-1&&n.splice(e,1)}var Hi,Ri=function(){function n(){this.dirty=!0,this._results=[],this.changes=new Ho,this.length=0}return n.prototype.map=function(n){return this._results.map(n)},n.prototype.filter=function(n){return this._results.filter(n)},n.prototype.find=function(n){return this._results.find(n)},n.prototype.reduce=function(n,t){return this._results.reduce(n,t)},n.prototype.forEach=function(n){this._results.forEach(n)},n.prototype.some=function(n){return this._results.some(n)},n.prototype.toArray=function(){return this._results.slice()},n.prototype[In()]=function(){return this._results[In()]()},n.prototype.toString=function(){return this._results.toString()},n.prototype.reset=function(n){this._results=function n(t){return t.reduce(function(t,e){var r=Array.isArray(e)?n(e):e;return t.concat(r)},[])}(n),this.dirty=!1,this.length=this._results.length,this.last=this._results[this.length-1],this.first=this._results[0]},n.prototype.notifyOnChanges=function(){this.changes.emit(this)},n.prototype.setDirty=function(){this.dirty=!0},n.prototype.destroy=function(){this.changes.complete(),this.changes.unsubscribe()},n}(),ji=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Li(n,Qr)},n}(),Li=Ee,zi=function(){function n(){}return n.__NG_ELEMENT_ID__=function(){return Fi()},n}(),Fi=function(){for(var n=[],t=0;t-1}(r)||"root"===o.providedIn&&r._def.isRoot))){var c=n._providers.length;return n._def.providersByKey[t.tokenKey]={flags:5120,value:u.factory,deps:[],index:c,token:t.token},n._providers[c]=fu,n._providers[c]=yu(n,n._def.providersByKey[t.tokenKey])}return 4&t.flags?e:n._parent.get(t.token,e)}finally{Fn(i)}}function yu(n,t){var e;switch(201347067&t.flags){case 512:e=function(n,t,e){var r=e.length;switch(r){case 0:return new t;case 1:return new t(vu(n,e[0]));case 2:return new t(vu(n,e[0]),vu(n,e[1]));case 3:return new t(vu(n,e[0]),vu(n,e[1]),vu(n,e[2]));default:for(var o=new Array(r),i=0;i=e.length)&&(t=e.length-1),t<0)return null;var r=e[t];return r.viewContainerParent=null,Cu(e,t),Cl.dirtyParentQueries(r),_u(r),r}function bu(n,t,e){var r=t?Fl(t,t.def.lastRenderRootNode):n.renderElement,o=e.renderer.parentNode(r),i=e.renderer.nextSibling(r);Wl(e,2,o,i,void 0)}function _u(n){Wl(n,3,null,null,void 0)}function wu(n,t,e){t>=n.length?n.push(e):n.splice(t,0,e)}function Cu(n,t){t>=n.length-1?n.pop():n.splice(t,1)}var Eu=new Object;function xu(n,t,e,r,o,i){return new Ou(n,t,e,r,o,i)}var Ou=function(n){function t(t,e,r,o,i,l){var u=n.call(this)||this;return u.selector=t,u.componentType=e,u._inputs=o,u._outputs=i,u.ngContentSelectors=l,u.viewDefFactory=r,u}return o(t,n),Object.defineProperty(t.prototype,"inputs",{get:function(){var n=[],t=this._inputs;for(var e in t)n.push({propName:e,templateName:t[e]});return n},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"outputs",{get:function(){var n=[];for(var t in this._outputs)n.push({propName:t,templateName:this._outputs[t]});return n},enumerable:!0,configurable:!0}),t.prototype.create=function(n,t,e,r){if(!r)throw new Error("ngModule should be provided");var o=Ql(this.viewDefFactory),i=o.nodes[0].element.componentProvider.nodeIndex,l=Cl.createRootView(n,t||[],e,o,r,Eu),u=bl(l,i).instance;return e&&l.renderer.setAttribute(ml(l,0).renderElement,"ng-version",to.full),new ku(l,new Iu(l),u)},t}(Fr),ku=function(n){function t(t,e,r){var o=n.call(this)||this;return o._view=t,o._viewRef=e,o._component=r,o._elDef=o._view.def.nodes[0],o.hostView=e,o.changeDetectorRef=e,o.instance=r,o}return o(t,n),Object.defineProperty(t.prototype,"location",{get:function(){return new Qr(ml(this._view,this._elDef.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"componentType",{get:function(){return this._component.constructor},enumerable:!0,configurable:!0}),t.prototype.destroy=function(){this._viewRef.destroy()},t.prototype.onDestroy=function(n){this._viewRef.onDestroy(n)},t}(zr);function Su(n,t,e){return new Au(n,t,e)}var Au=function(){function n(n,t,e){this._view=n,this._elDef=t,this._data=e,this._embeddedViews=[]}return Object.defineProperty(n.prototype,"element",{get:function(){return new Qr(this._data.renderElement)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"injector",{get:function(){return new Mu(this._view,this._elDef)},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"parentInjector",{get:function(){for(var n=this._view,t=this._elDef.parent;!t&&n;)t=zl(n),n=n.parent;return n?new Mu(n,t):new Mu(this._view,null)},enumerable:!0,configurable:!0}),n.prototype.clear=function(){for(var n=this._embeddedViews.length-1;n>=0;n--){var t=mu(this._data,n);Cl.destroyView(t)}},n.prototype.get=function(n){var t=this._embeddedViews[n];if(t){var e=new Iu(t);return e.attachToViewContainerRef(this),e}return null},Object.defineProperty(n.prototype,"length",{get:function(){return this._embeddedViews.length},enumerable:!0,configurable:!0}),n.prototype.createEmbeddedView=function(n,t,e){var r=n.createEmbeddedView(t||{});return this.insert(r,e),r},n.prototype.createComponent=function(n,t,e,r,o){var i=e||this.parentInjector;o||n instanceof $r||(o=i.get(qe));var l=n.create(i,r,void 0,o);return this.insert(l.hostView,t),l},n.prototype.insert=function(n,t){if(n.destroyed)throw new Error("Cannot insert a destroyed View in a ViewContainer!");var e,r,o,i,l=n;return i=(e=this._data).viewContainer._embeddedViews,null==(r=t)&&(r=i.length),(o=l._view).viewContainerParent=this._view,wu(i,r,o),function(n,t){var e=Ll(t);if(e&&e!==n&&!(16&t.state)){t.state|=16;var r=e.template._projectedViews;r||(r=e.template._projectedViews=[]),r.push(t),function(n,e){if(!(4&e.flags)){t.parent.def.nodeFlags|=4,e.flags|=4;for(var r=e.parent;r;)r.childFlags|=4,r=r.parent}}(0,t.parentNodeDef)}}(e,o),Cl.dirtyParentQueries(o),bu(e,r>0?i[r-1]:null,o),l.attachToViewContainerRef(this),n},n.prototype.move=function(n,t){if(n.destroyed)throw new Error("Cannot move a destroyed View in a ViewContainer!");var e,r,o,i,l,u=this._embeddedViews.indexOf(n._view);return o=t,l=(i=(e=this._data).viewContainer._embeddedViews)[r=u],Cu(i,r),null==o&&(o=i.length),wu(i,o,l),Cl.dirtyParentQueries(l),_u(l),bu(e,o>0?i[o-1]:null,l),n},n.prototype.indexOf=function(n){return this._embeddedViews.indexOf(n._view)},n.prototype.remove=function(n){var t=mu(this._data,n);t&&Cl.destroyView(t)},n.prototype.detach=function(n){var t=mu(this._data,n);return t?new Iu(t):null},n}();function Tu(n){return new Iu(n)}var Iu=function(){function n(n){this._view=n,this._viewContainerRef=null,this._appRef=null}return Object.defineProperty(n.prototype,"rootNodes",{get:function(){return Wl(this._view,0,void 0,void 0,n=[]),n;var n},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"context",{get:function(){return this._view.context},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"destroyed",{get:function(){return 0!=(128&this._view.state)},enumerable:!0,configurable:!0}),n.prototype.markForCheck=function(){Hl(this._view)},n.prototype.detach=function(){this._view.state&=-5},n.prototype.detectChanges=function(){var n=this._view.root.rendererFactory;n.begin&&n.begin();try{Cl.checkAndUpdateView(this._view)}finally{n.end&&n.end()}},n.prototype.checkNoChanges=function(){Cl.checkNoChangesView(this._view)},n.prototype.reattach=function(){this._view.state|=4},n.prototype.onDestroy=function(n){this._view.disposables||(this._view.disposables=[]),this._view.disposables.push(n)},n.prototype.destroy=function(){this._appRef?this._appRef.detachView(this):this._viewContainerRef&&this._viewContainerRef.detach(this._viewContainerRef.indexOf(this)),Cl.destroyView(this._view)},n.prototype.detachFromAppRef=function(){this._appRef=null,_u(this._view),Cl.dirtyParentQueries(this._view)},n.prototype.attachToAppRef=function(n){if(this._viewContainerRef)throw new Error("This view is already attached to a ViewContainer!");this._appRef=n},n.prototype.attachToViewContainerRef=function(n){if(this._appRef)throw new Error("This view is already attached directly to the ApplicationRef!");this._viewContainerRef=n},n}();function Pu(n,t){return new Nu(n,t)}var Nu=function(n){function t(t,e){var r=n.call(this)||this;return r._parentView=t,r._def=e,r}return o(t,n),t.prototype.createEmbeddedView=function(n){return new Iu(Cl.createEmbeddedView(this._parentView,this._def,this._def.element.template,n))},Object.defineProperty(t.prototype,"elementRef",{get:function(){return new Qr(ml(this._parentView,this._def.nodeIndex).renderElement)},enumerable:!0,configurable:!0}),t}(Ro);function Du(n,t){return new Mu(n,t)}var Mu=function(){function n(n,t){this.view=n,this.elDef=t}return n.prototype.get=function(n,t){return void 0===t&&(t=Ne.THROW_IF_NOT_FOUND),Cl.resolveDep(this.view,this.elDef,!!this.elDef&&0!=(33554432&this.elDef.flags),{flags:0,token:n,tokenKey:Al(n)},t)},n}();function Vu(n,t){var e=n.def.nodes[t];if(1&e.flags){var r=ml(n,e.nodeIndex);return e.element.template?r.template:r.renderElement}if(2&e.flags)return yl(n,e.nodeIndex).renderText;if(20240&e.flags)return bl(n,e.nodeIndex).instance;throw new Error("Illegal state: read nodeValue for node index "+t)}function Hu(n){return new Ru(n.renderer)}var Ru=function(){function n(n){this.delegate=n}return n.prototype.selectRootElement=function(n){return this.delegate.selectRootElement(n)},n.prototype.createElement=function(n,t){var e=a(tu(t),2),r=this.delegate.createElement(e[1],e[0]);return n&&this.delegate.appendChild(n,r),r},n.prototype.createViewRoot=function(n){return n},n.prototype.createTemplateAnchor=function(n){var t=this.delegate.createComment("");return n&&this.delegate.appendChild(n,t),t},n.prototype.createText=function(n,t){var e=this.delegate.createText(t);return n&&this.delegate.appendChild(n,e),e},n.prototype.projectNodes=function(n,t){for(var e=0;e0,t.provider.value,t.provider.deps);if(t.outputs.length)for(var r=0;r0,r=t.provider;switch(201347067&t.flags){case 512:return es(n,t.parent,e,r.value,r.deps);case 1024:return function(n,t,e,r,o){var i=o.length;switch(i){case 0:return r();case 1:return r(os(n,t,e,o[0]));case 2:return r(os(n,t,e,o[0]),os(n,t,e,o[1]));case 3:return r(os(n,t,e,o[0]),os(n,t,e,o[1]),os(n,t,e,o[2]));default:for(var l=Array(i),u=0;u0)a=g,_s(g)||(c=g);else for(;a&&d===a.nodeIndex+a.childCount;){var m=a.parent;m&&(m.childFlags|=a.childFlags,m.childMatchedQueries|=a.childMatchedQueries),c=(a=m)&&_s(a)?a.renderParent:a}}return{factory:null,nodeFlags:l,rootNodeFlags:u,nodeMatchedQueries:s,flags:n,nodes:t,updateDirectives:e||kl,updateRenderer:r||kl,handleEvent:function(n,e,r,o){return t[e].element.handleEvent(n,r,o)},bindingCount:o,outputCount:i,lastRenderRootNode:p}}function _s(n){return 0!=(1&n.flags)&&null===n.element.name}function ws(n,t,e){var r=t.element&&t.element.template;if(r){if(!r.lastRenderRootNode)throw new Error("Illegal State: Embedded templates without nodes are not allowed!");if(r.lastRenderRootNode&&16777216&r.lastRenderRootNode.flags)throw new Error("Illegal State: Last root node of a template can't have embedded views, at index "+t.nodeIndex+"!")}if(20224&t.flags&&0==(1&(n?n.flags:0)))throw new Error("Illegal State: StaticProvider/Directive nodes need to be children of elements or anchors, at index "+t.nodeIndex+"!");if(t.query){if(67108864&t.flags&&(!n||0==(16384&n.flags)))throw new Error("Illegal State: Content Query nodes need to be children of directives, at index "+t.nodeIndex+"!");if(134217728&t.flags&&n)throw new Error("Illegal State: View Query nodes have to be top level nodes, at index "+t.nodeIndex+"!")}if(t.childCount){var o=n?n.nodeIndex+n.childCount:e-1;if(t.nodeIndex<=o&&t.nodeIndex+t.childCount>o)throw new Error("Illegal State: childCount of node leads outside of parent, at index "+t.nodeIndex+"!")}}function Cs(n,t,e,r){var o=Os(n.root,n.renderer,n,t,e);return ks(o,n.component,r),Ss(o),o}function Es(n,t,e){var r=Os(n,n.renderer,null,null,t);return ks(r,e,e),Ss(r),r}function xs(n,t,e,r){var o,i=t.element.componentRendererType;return o=i?n.root.rendererFactory.createRenderer(r,i):n.root.renderer,Os(n.root,o,n,t.element.componentProvider,e)}function Os(n,t,e,r,o){var i=new Array(o.nodes.length),l=o.outputCount?new Array(o.outputCount):null;return{def:o,parent:e,viewContainerParent:null,parentNodeDef:r,context:null,component:null,nodes:i,state:13,root:n,renderer:t,oldValues:new Array(o.bindingCount),disposables:l,initIndex:-1}}function ks(n,t,e){n.component=t,n.context=e}function Ss(n){var t;Bl(n)&&(t=ml(n.parent,n.parentNodeDef.parent.nodeIndex).renderElement);for(var e=n.def,r=n.nodes,o=0;o0&&cu(n,t,0,e)&&(p=!0),h>1&&cu(n,t,1,r)&&(p=!0),h>2&&cu(n,t,2,o)&&(p=!0),h>3&&cu(n,t,3,i)&&(p=!0),h>4&&cu(n,t,4,l)&&(p=!0),h>5&&cu(n,t,5,u)&&(p=!0),h>6&&cu(n,t,6,s)&&(p=!0),h>7&&cu(n,t,7,a)&&(p=!0),h>8&&cu(n,t,8,c)&&(p=!0),h>9&&cu(n,t,9,f)&&(p=!0),p}(n,t,e,r,o,i,l,u,s,a,c,f);case 2:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=!1,p=t.bindings,d=p.length;if(d>0&&Ml(n,t,0,e)&&(h=!0),d>1&&Ml(n,t,1,r)&&(h=!0),d>2&&Ml(n,t,2,o)&&(h=!0),d>3&&Ml(n,t,3,i)&&(h=!0),d>4&&Ml(n,t,4,l)&&(h=!0),d>5&&Ml(n,t,5,u)&&(h=!0),d>6&&Ml(n,t,6,s)&&(h=!0),d>7&&Ml(n,t,7,a)&&(h=!0),d>8&&Ml(n,t,8,c)&&(h=!0),d>9&&Ml(n,t,9,f)&&(h=!0),h){var g=t.text.prefix;d>0&&(g+=ms(e,p[0])),d>1&&(g+=ms(r,p[1])),d>2&&(g+=ms(o,p[2])),d>3&&(g+=ms(i,p[3])),d>4&&(g+=ms(l,p[4])),d>5&&(g+=ms(u,p[5])),d>6&&(g+=ms(s,p[6])),d>7&&(g+=ms(a,p[7])),d>8&&(g+=ms(c,p[8])),d>9&&(g+=ms(f,p[9]));var v=yl(n,t.nodeIndex).renderText;n.renderer.setValue(v,g)}return h}(n,t,e,r,o,i,l,u,s,a,c,f);case 16384:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=bl(n,t.nodeIndex),p=h.instance,d=!1,g=void 0,v=t.bindings.length;return v>0&&Dl(n,t,0,e)&&(d=!0,g=ls(n,h,t,0,e,g)),v>1&&Dl(n,t,1,r)&&(d=!0,g=ls(n,h,t,1,r,g)),v>2&&Dl(n,t,2,o)&&(d=!0,g=ls(n,h,t,2,o,g)),v>3&&Dl(n,t,3,i)&&(d=!0,g=ls(n,h,t,3,i,g)),v>4&&Dl(n,t,4,l)&&(d=!0,g=ls(n,h,t,4,l,g)),v>5&&Dl(n,t,5,u)&&(d=!0,g=ls(n,h,t,5,u,g)),v>6&&Dl(n,t,6,s)&&(d=!0,g=ls(n,h,t,6,s,g)),v>7&&Dl(n,t,7,a)&&(d=!0,g=ls(n,h,t,7,a,g)),v>8&&Dl(n,t,8,c)&&(d=!0,g=ls(n,h,t,8,c,g)),v>9&&Dl(n,t,9,f)&&(d=!0,g=ls(n,h,t,9,f,g)),g&&p.ngOnChanges(g),65536&t.flags&&vl(n,256,t.nodeIndex)&&p.ngOnInit(),262144&t.flags&&p.ngDoCheck(),d}(n,t,e,r,o,i,l,u,s,a,c,f);case 32:case 64:case 128:return function(n,t,e,r,o,i,l,u,s,a,c,f){var h=t.bindings,p=!1,d=h.length;if(d>0&&Ml(n,t,0,e)&&(p=!0),d>1&&Ml(n,t,1,r)&&(p=!0),d>2&&Ml(n,t,2,o)&&(p=!0),d>3&&Ml(n,t,3,i)&&(p=!0),d>4&&Ml(n,t,4,l)&&(p=!0),d>5&&Ml(n,t,5,u)&&(p=!0),d>6&&Ml(n,t,6,s)&&(p=!0),d>7&&Ml(n,t,7,a)&&(p=!0),d>8&&Ml(n,t,8,c)&&(p=!0),d>9&&Ml(n,t,9,f)&&(p=!0),p){var g=_l(n,t.nodeIndex),v=void 0;switch(201347067&t.flags){case 32:v=new Array(h.length),d>0&&(v[0]=e),d>1&&(v[1]=r),d>2&&(v[2]=o),d>3&&(v[3]=i),d>4&&(v[4]=l),d>5&&(v[5]=u),d>6&&(v[6]=s),d>7&&(v[7]=a),d>8&&(v[8]=c),d>9&&(v[9]=f);break;case 64:v={},d>0&&(v[h[0].name]=e),d>1&&(v[h[1].name]=r),d>2&&(v[h[2].name]=o),d>3&&(v[h[3].name]=i),d>4&&(v[h[4].name]=l),d>5&&(v[h[5].name]=u),d>6&&(v[h[6].name]=s),d>7&&(v[h[7].name]=a),d>8&&(v[h[8].name]=c),d>9&&(v[h[9].name]=f);break;case 128:var y=e;switch(d){case 1:v=y.transform(e);break;case 2:v=y.transform(r);break;case 3:v=y.transform(r,o);break;case 4:v=y.transform(r,o,i);break;case 5:v=y.transform(r,o,i,l);break;case 6:v=y.transform(r,o,i,l,u);break;case 7:v=y.transform(r,o,i,l,u,s);break;case 8:v=y.transform(r,o,i,l,u,s,a);break;case 9:v=y.transform(r,o,i,l,u,s,a,c);break;case 10:v=y.transform(r,o,i,l,u,s,a,c,f)}}g.value=v}return p}(n,t,e,r,o,i,l,u,s,a,c,f);default:throw"unreachable"}}(n,t,r,o,i,l,u,s,a,f,h,p):function(n,t,e){switch(201347067&t.flags){case 1:return function(n,t,e){for(var r=!1,o=0;o0&&Vl(n,t,0,e),h>1&&Vl(n,t,1,r),h>2&&Vl(n,t,2,o),h>3&&Vl(n,t,3,i),h>4&&Vl(n,t,4,l),h>5&&Vl(n,t,5,u),h>6&&Vl(n,t,6,s),h>7&&Vl(n,t,7,a),h>8&&Vl(n,t,8,c),h>9&&Vl(n,t,9,f)}(n,t,r,o,i,l,u,s,a,c,f,h):function(n,t,e){for(var r=0;r0){var i=new Set(n.modules);Ws.forEach(function(t,r){if(i.has(bn(r).providedIn)){var o={token:r,flags:t.flags|(e?4096:0),deps:Zl(t.deps),value:t.value,index:n.providers.length};n.providers.push(o),n.providersByKey[Al(r)]=o}})}}(n=n.factory(function(){return kl})),n):n}(r))}var Qs=new Map,Ws=new Map,Ks=new Map;function Js(n){var t;Qs.set(n.token,n),"function"==typeof n.token&&(t=bn(n.token))&&"function"==typeof t.providedIn&&Ws.set(n.token,n)}function Ys(n,t){var e=Ql(t.viewDefFactory),r=Ql(e.nodes[0].element.componentView);Ks.set(n,r)}function Xs(){Qs.clear(),Ws.clear(),Ks.clear()}function na(n){if(0===Qs.size)return n;var t=function(n){for(var t=[],e=null,r=0;r=this.currentHistoricCoverage.lcq)return!1}else if("branchCoverageIncreaseOnly"===t){var r=this.branchCoverage;if(isNaN(r)||r<=this.currentHistoricCoverage.bcq)return!1}else if("branchCoverageDecreaseOnly"===t&&(r=this.branchCoverage,isNaN(r)||r>=this.currentHistoricCoverage.bcq))return!1;return!0},t.prototype.updateCurrentHistoricCoverage=function(n){if(this.currentHistoricCoverage=null,""!==n)for(var t=0;t-1&&null===e,r}return o(t,n),t.prototype.visible=function(n,t){if(""!==n&&this.name.toLowerCase().indexOf(n.toLowerCase())>-1)return!0;for(var e=0;et&&(r[o].collapsed=n.settings.collapseStates[t]),t++,e(r[o].subElements)};e(this.codeElements)},n}(),La=new I(function(n){return n.complete()}),za=function(n){function t(t,e){var r=n.call(this,t)||this;r.sources=e,r.completed=0,r.haveValues=0;var o=e.length;r.values=new Array(o);for(var i=0;i0},t.prototype.tagName=function(n){return n.tagName},t.prototype.attributeMap=function(n){for(var t=new Map,e=n.attributes,r=0;r0;l||(l=n[i]=[]);var s=$c(t)?Zone.root:Zone.current;if(0===l.length)l.push({zone:s,handler:o});else{for(var a=!1,c=0;c-1},t}(kc),tf=["alt","control","meta","shift"],ef={alt:function(n){return n.altKey},control:function(n){return n.ctrlKey},meta:function(n){return n.metaKey},shift:function(n){return n.shiftKey}},rf=function(n){function t(t){return n.call(this,t)||this}var e;return o(t,n),e=t,t.prototype.supports=function(n){return null!=e.parseEventName(n)},t.prototype.addEventListener=function(n,t,r){var o=e.parseEventName(t),i=e.eventCallback(o.fullKey,r,this.manager.getZone());return this.manager.getZone().runOutsideAngular(function(){return uc().onAndCancel(n,o.domEventName,i)})},t.parseEventName=function(n){var t=n.toLowerCase().split("."),r=t.shift();if(0===t.length||"keydown"!==r&&"keyup"!==r)return null;var o=e._normalizeKey(t.pop()),i="";if(tf.forEach(function(n){var e=t.indexOf(n);e>-1&&(t.splice(e,1),i+=n+".")}),i+=o,0!=t.length||0===o.length)return null;var l={};return l.domEventName=r,l.fullKey=i,l},t.getEventFullKey=function(n){var t="",e=uc().getEventKey(n);return" "===(e=e.toLowerCase())?e="space":"."===e&&(e="dot"),tf.forEach(function(r){r!=e&&(0,ef[r])(n)&&(t+=r+".")}),t+=e},t.eventCallback=function(n,t,r){return function(o){e.getEventFullKey(o)===n&&r.runGuarded(function(){return t(o)})}},t._normalizeKey=function(n){switch(n){case"esc":return"escape";default:return n}},t}(kc),of=function(){return function(){}}(),lf=function(n){function t(t){var e=n.call(this)||this;return e._doc=t,e}return o(t,n),t.prototype.sanitize=function(n,t){if(null==t)return null;switch(n){case Lo.NONE:return t;case Lo.HTML:return t instanceof sf?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"HTML"),function(n,t){var e=null;try{_o=_o||new po(n);var r=t?String(t):"";e=_o.getInertBodyElement(r);var o=5,i=r;do{if(0===o)throw new Error("Failed to sanitize html because the input is unstable");o--,r=i,i=e.innerHTML,e=_o.getInertBodyElement(r)}while(r!==i);var l=new To,u=l.sanitizeChildren(Do(e)||e);return ho()&&l.sanitizedSomething&&console.warn("WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."),u}finally{if(e)for(var s=Do(e)||e;s.firstChild;)s.removeChild(s.firstChild)}}(this._doc,String(t)));case Lo.STYLE:return t instanceof af?t.changingThisBreaksApplicationSecurity:(this.checkNotSafeValue(t,"Style"),function(n){if(!(n=String(n).trim()))return"";var t=n.match(Bo);return t&&yo(t[1])===t[1]||n.match(Fo)&&function(n){for(var t=!0,e=!0,r=0;rn?{max:{max:n,actual:t.value}}:null}},n.required=function(n){return mf(n.value)?{required:!0}:null},n.requiredTrue=function(n){return!0===n.value?null:{required:!0}},n.email=function(n){return mf(n.value)?null:bf.test(n.value)?null:{email:!0}},n.minLength=function(n){return function(t){if(mf(t.value))return null;var e=t.value?t.value.length:0;return en?{maxlength:{requiredLength:n,actualLength:e}}:null}},n.pattern=function(t){return t?("string"==typeof t?(r="","^"!==t.charAt(0)&&(r+="^"),r+=t,"$"!==t.charAt(t.length-1)&&(r+="$"),e=new RegExp(r)):(r=t.toString(),e=t),function(n){if(mf(n.value))return null;var t=n.value;return e.test(t)?null:{pattern:{requiredPattern:r,actualValue:t}}}):n.nullValidator;var e,r},n.nullValidator=function(n){return null},n.compose=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return Ef(function(n,e){return t.map(function(t){return t(n)})}(n))}},n.composeAsync=function(n){if(!n)return null;var t=n.filter(wf);return 0==t.length?null:function(n){return function n(){for(var t,e=[],r=0;r=0;--t)if(this._accessors[t][1]===n)return void this._accessors.splice(t,1)},n.prototype.select=function(n){var t=this;this._accessors.forEach(function(e){t._isSameGroup(e,n)&&e[1]!==n&&e[1].fireUncheck(n.value)})},n.prototype._isSameGroup=function(n,t){return!!n[0].control&&n[0]._parent===t._control._parent&&n[1].name===t.name},n}(),Mf=function(){function n(n,t,e,r){this._renderer=n,this._elementRef=t,this._registry=e,this._injector=r,this.onChange=function(){},this.onTouched=function(){}}return n.prototype.ngOnInit=function(){this._control=this._injector.get(Nf),this._checkName(),this._registry.add(this._control,this)},n.prototype.ngOnDestroy=function(){this._registry.remove(this)},n.prototype.writeValue=function(n){this._state=n===this.value,this._renderer.setProperty(this._elementRef.nativeElement,"checked",this._state)},n.prototype.registerOnChange=function(n){var t=this;this._fn=n,this.onChange=function(){n(t.value),t._registry.select(t)}},n.prototype.fireUncheck=function(n){this.writeValue(n)},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._checkName=function(){this.name&&this.formControlName&&this.name!==this.formControlName&&this._throwNameError(),!this.name&&this.formControlName&&(this.name=this.formControlName)},n.prototype._throwNameError=function(){throw new Error('\n If you define both a name and a formControlName attribute on your radio button, their values\n must match. Ex: \n ')},n}(),Vf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this.onChange=function(n){},this.onTouched=function(){}}return n.prototype.writeValue=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"value",parseFloat(n))},n.prototype.registerOnChange=function(n){this.onChange=function(t){n(""==t?null:parseFloat(t))}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n}(),Hf='\n

\n
\n \n
\n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n person: new FormGroup({ firstName: new FormControl() })\n });',Rf='\n
\n
\n \n
\n
';function jf(n,t){return null==n?""+t:(t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Lf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){this.value=n;var t=this._getOptionId(n);null==t&&this._renderer.setProperty(this._elementRef.nativeElement,"selectedIndex",-1);var e=jf(t,n);this._renderer.setProperty(this._elementRef.nativeElement,"value",e)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){t.value=t._getOptionValue(e),n(t.value)}},n.prototype.registerOnTouched=function(n){this.onTouched=n},n.prototype.setDisabledState=function(n){this._renderer.setProperty(this._elementRef.nativeElement,"disabled",n)},n.prototype._registerOption=function(){return(this._idCounter++).toString()},n.prototype._getOptionId=function(n){var t,e;try{for(var r=s(Array.from(this._optionMap.keys())),o=r.next();!o.done;o=r.next()){var i=o.value;if(this._compareWith(this._optionMap.get(i),n))return i}}catch(l){t={error:l}}finally{try{o&&!o.done&&(e=r.return)&&e.call(r)}finally{if(t)throw t.error}}return null},n.prototype._getOptionValue=function(n){var t=function(n){return n.split(":")[0]}(n);return this._optionMap.has(t)?this._optionMap.get(t):n},n}(),zf=function(){function n(n,t,e){this._element=n,this._renderer=t,this._select=e,this._select&&(this.id=this._select._registerOption())}return Object.defineProperty(n.prototype,"ngValue",{set:function(n){null!=this._select&&(this._select._optionMap.set(this.id,n),this._setElementValue(jf(this.id,n)),this._select.writeValue(this._select.value))},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"value",{set:function(n){this._setElementValue(n),this._select&&this._select.writeValue(this._select.value)},enumerable:!0,configurable:!0}),n.prototype._setElementValue=function(n){this._renderer.setProperty(this._element.nativeElement,"value",n)},n.prototype.ngOnDestroy=function(){this._select&&(this._select._optionMap.delete(this.id),this._select.writeValue(this._select.value))},n}();function Ff(n,t){return null==n?""+t:("string"==typeof t&&(t="'"+t+"'"),t&&"object"==typeof t&&(t="Object"),(n+": "+t).slice(0,50))}var Bf=function(){function n(n,t){this._renderer=n,this._elementRef=t,this._optionMap=new Map,this._idCounter=0,this.onChange=function(n){},this.onTouched=function(){},this._compareWith=Nn}return Object.defineProperty(n.prototype,"compareWith",{set:function(n){if("function"!=typeof n)throw new Error("compareWith must be a function, but received "+JSON.stringify(n));this._compareWith=n},enumerable:!0,configurable:!0}),n.prototype.writeValue=function(n){var t,e=this;if(this.value=n,Array.isArray(n)){var r=n.map(function(n){return e._getOptionId(n)});t=function(n,t){n._setSelected(r.indexOf(t.toString())>-1)}}else t=function(n,t){n._setSelected(!1)};this._optionMap.forEach(t)},n.prototype.registerOnChange=function(n){var t=this;this.onChange=function(e){var r=[];if(e.hasOwnProperty("selectedOptions"))for(var o=e.selectedOptions,i=0;i1?"path: '"+n.path.join(" -> ")+"'":n.path[0]?"name: '"+n.path+"'":"unspecified name attribute",new Error(t+" "+e)}function Qf(n){return null!=n?_f.compose(n.map(Af)):null}function Wf(n){return null!=n?_f.composeAsync(n.map(Tf)):null}var Kf=[Of,Vf,If,Lf,Bf,Mf],Jf=function(n){function t(){return null!==n&&n.apply(this,arguments)||this}return o(t,n),t.prototype.ngOnInit=function(){this._checkParentType(),this.formDirective.addFormGroup(this)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeFormGroup(this)},Object.defineProperty(t.prototype,"control",{get:function(){return this.formDirective.getFormGroup(this)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return Gf(this.name,this._parent)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._validators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._asyncValidators)},enumerable:!0,configurable:!0}),t.prototype._checkParentType=function(){},t}(yf),Yf=function(n){function t(t){return n.call(this,t)||this}return o(t,n),t}(function(){function n(n){this._cd=n}return Object.defineProperty(n.prototype,"ngClassUntouched",{get:function(){return!!this._cd.control&&this._cd.control.untouched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassTouched",{get:function(){return!!this._cd.control&&this._cd.control.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPristine",{get:function(){return!!this._cd.control&&this._cd.control.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassDirty",{get:function(){return!!this._cd.control&&this._cd.control.dirty},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassValid",{get:function(){return!!this._cd.control&&this._cd.control.valid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassInvalid",{get:function(){return!!this._cd.control&&this._cd.control.invalid},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"ngClassPending",{get:function(){return!!this._cd.control&&this._cd.control.pending},enumerable:!0,configurable:!0}),n}());function Xf(n){var t=th(n)?n.validators:n;return Array.isArray(t)?Qf(t):t||null}function nh(n,t){var e=th(t)?t.asyncValidators:n;return Array.isArray(e)?Wf(e):e||null}function th(n){return null!=n&&!Array.isArray(n)&&"object"==typeof n}var eh=function(){function n(n,t){this.validator=n,this.asyncValidator=t,this._onCollectionChange=function(){},this.pristine=!0,this.touched=!1,this._onDisabledChange=[]}return Object.defineProperty(n.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"valid",{get:function(){return"VALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"invalid",{get:function(){return"INVALID"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"pending",{get:function(){return"PENDING"==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"disabled",{get:function(){return"DISABLED"===this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"enabled",{get:function(){return"DISABLED"!==this.status},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"dirty",{get:function(){return!this.pristine},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"untouched",{get:function(){return!this.touched},enumerable:!0,configurable:!0}),Object.defineProperty(n.prototype,"updateOn",{get:function(){return this._updateOn?this._updateOn:this.parent?this.parent.updateOn:"change"},enumerable:!0,configurable:!0}),n.prototype.setValidators=function(n){this.validator=Xf(n)},n.prototype.setAsyncValidators=function(n){this.asyncValidator=nh(n)},n.prototype.clearValidators=function(){this.validator=null},n.prototype.clearAsyncValidators=function(){this.asyncValidator=null},n.prototype.markAsTouched=function(n){void 0===n&&(n={}),this.touched=!0,this._parent&&!n.onlySelf&&this._parent.markAsTouched(n)},n.prototype.markAsUntouched=function(n){void 0===n&&(n={}),this.touched=!1,this._pendingTouched=!1,this._forEachChild(function(n){n.markAsUntouched({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype.markAsDirty=function(n){void 0===n&&(n={}),this.pristine=!1,this._parent&&!n.onlySelf&&this._parent.markAsDirty(n)},n.prototype.markAsPristine=function(n){void 0===n&&(n={}),this.pristine=!0,this._pendingDirty=!1,this._forEachChild(function(n){n.markAsPristine({onlySelf:!0})}),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype.markAsPending=function(n){void 0===n&&(n={}),this.status="PENDING",!1!==n.emitEvent&&this.statusChanges.emit(this.status),this._parent&&!n.onlySelf&&this._parent.markAsPending(n)},n.prototype.disable=function(n){void 0===n&&(n={}),this.status="DISABLED",this.errors=null,this._forEachChild(function(t){t.disable(i({},n,{onlySelf:!0}))}),this._updateValue(),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!0)})},n.prototype.enable=function(n){void 0===n&&(n={}),this.status="VALID",this._forEachChild(function(t){t.enable(i({},n,{onlySelf:!0}))}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent}),this._updateAncestors(n),this._onDisabledChange.forEach(function(n){return n(!1)})},n.prototype._updateAncestors=function(n){this._parent&&!n.onlySelf&&(this._parent.updateValueAndValidity(n),this._parent._updatePristine(),this._parent._updateTouched())},n.prototype.setParent=function(n){this._parent=n},n.prototype.updateValueAndValidity=function(n){void 0===n&&(n={}),this._setInitialStatus(),this._updateValue(),this.enabled&&(this._cancelExistingSubscription(),this.errors=this._runValidator(),this.status=this._calculateStatus(),"VALID"!==this.status&&"PENDING"!==this.status||this._runAsyncValidator(n.emitEvent)),!1!==n.emitEvent&&(this.valueChanges.emit(this.value),this.statusChanges.emit(this.status)),this._parent&&!n.onlySelf&&this._parent.updateValueAndValidity(n)},n.prototype._updateTreeValidity=function(n){void 0===n&&(n={emitEvent:!0}),this._forEachChild(function(t){return t._updateTreeValidity(n)}),this.updateValueAndValidity({onlySelf:!0,emitEvent:n.emitEvent})},n.prototype._setInitialStatus=function(){this.status=this._allControlsDisabled()?"DISABLED":"VALID"},n.prototype._runValidator=function(){return this.validator?this.validator(this):null},n.prototype._runAsyncValidator=function(n){var t=this;if(this.asyncValidator){this.status="PENDING";var e=Cf(this.asyncValidator(this));this._asyncValidationSubscription=e.subscribe(function(e){return t.setErrors(e,{emitEvent:n})})}},n.prototype._cancelExistingSubscription=function(){this._asyncValidationSubscription&&this._asyncValidationSubscription.unsubscribe()},n.prototype.setErrors=function(n,t){void 0===t&&(t={}),this.errors=n,this._updateControlsErrors(!1!==t.emitEvent)},n.prototype.get=function(n){return function(n,t,e){return null==t?null:(t instanceof Array||(t=t.split(".")),t instanceof Array&&0===t.length?null:t.reduce(function(n,t){return n instanceof oh?n.controls.hasOwnProperty(t)?n.controls[t]:null:n instanceof ih&&n.at(t)||null},n))}(this,n)},n.prototype.getError=function(n,t){var e=t?this.get(t):this;return e&&e.errors?e.errors[n]:null},n.prototype.hasError=function(n,t){return!!this.getError(n,t)},Object.defineProperty(n.prototype,"root",{get:function(){for(var n=this;n._parent;)n=n._parent;return n},enumerable:!0,configurable:!0}),n.prototype._updateControlsErrors=function(n){this.status=this._calculateStatus(),n&&this.statusChanges.emit(this.status),this._parent&&this._parent._updateControlsErrors(n)},n.prototype._initObservables=function(){this.valueChanges=new Ho,this.statusChanges=new Ho},n.prototype._calculateStatus=function(){return this._allControlsDisabled()?"DISABLED":this.errors?"INVALID":this._anyControlsHaveStatus("PENDING")?"PENDING":this._anyControlsHaveStatus("INVALID")?"INVALID":"VALID"},n.prototype._anyControlsHaveStatus=function(n){return this._anyControls(function(t){return t.status===n})},n.prototype._anyControlsDirty=function(){return this._anyControls(function(n){return n.dirty})},n.prototype._anyControlsTouched=function(){return this._anyControls(function(n){return n.touched})},n.prototype._updatePristine=function(n){void 0===n&&(n={}),this.pristine=!this._anyControlsDirty(),this._parent&&!n.onlySelf&&this._parent._updatePristine(n)},n.prototype._updateTouched=function(n){void 0===n&&(n={}),this.touched=this._anyControlsTouched(),this._parent&&!n.onlySelf&&this._parent._updateTouched(n)},n.prototype._isBoxedValue=function(n){return"object"==typeof n&&null!==n&&2===Object.keys(n).length&&"value"in n&&"disabled"in n},n.prototype._registerOnCollectionChange=function(n){this._onCollectionChange=n},n.prototype._setUpdateStrategy=function(n){th(n)&&null!=n.updateOn&&(this._updateOn=n.updateOn)},n}(),rh=function(n){function t(t,e,r){void 0===t&&(t=null);var o=n.call(this,Xf(e),nh(r,e))||this;return o._onChange=[],o._applyFormState(t),o._setUpdateStrategy(e),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o._initObservables(),o}return o(t,n),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this.value=this._pendingValue=n,this._onChange.length&&!1!==t.emitModelToViewChange&&this._onChange.forEach(function(n){return n(e.value,!1!==t.emitViewToModelChange)}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){void 0===t&&(t={}),this.setValue(n,t)},t.prototype.reset=function(n,t){void 0===n&&(n=null),void 0===t&&(t={}),this._applyFormState(n),this.markAsPristine(t),this.markAsUntouched(t),this.setValue(this.value,t),this._pendingChange=!1},t.prototype._updateValue=function(){},t.prototype._anyControls=function(n){return!1},t.prototype._allControlsDisabled=function(){return this.disabled},t.prototype.registerOnChange=function(n){this._onChange.push(n)},t.prototype._clearChangeFns=function(){this._onChange=[],this._onDisabledChange=[],this._onCollectionChange=function(){}},t.prototype.registerOnDisabledChange=function(n){this._onDisabledChange.push(n)},t.prototype._forEachChild=function(n){},t.prototype._syncPendingControls=function(){return!("submit"!==this.updateOn||(this._pendingDirty&&this.markAsDirty(),this._pendingTouched&&this.markAsTouched(),!this._pendingChange)||(this.setValue(this._pendingValue,{onlySelf:!0,emitModelToViewChange:!1}),0))},t.prototype._applyFormState=function(n){this._isBoxedValue(n)?(this.value=this._pendingValue=n.value,n.disabled?this.disable({onlySelf:!0,emitEvent:!1}):this.enable({onlySelf:!0,emitEvent:!1})):this.value=this._pendingValue=n},t}(eh),oh=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.registerControl=function(n,t){return this.controls[n]?this.controls[n]:(this.controls[n]=t,t.setParent(this),t._registerOnCollectionChange(this._onCollectionChange),t)},t.prototype.addControl=function(n,t){this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.removeControl=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),delete this.controls[n],t&&this.registerControl(n,t),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.contains=function(n){return this.controls.hasOwnProperty(n)&&this.controls[n].enabled},t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),Object.keys(n).forEach(function(r){e._throwIfControlMissing(r),e.controls[r].setValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),Object.keys(n).forEach(function(r){e.controls[r]&&e.controls[r].patchValue(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n={}),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this._reduceChildren({},function(n,t,e){return n[e]=t instanceof rh?t.value:t.getRawValue(),n})},t.prototype._syncPendingControls=function(){var n=this._reduceChildren(!1,function(n,t){return!!t._syncPendingControls()||n});return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!Object.keys(this.controls).length)throw new Error("\n There are no form controls registered with this group yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.controls[n])throw new Error("Cannot find form control with name: "+n+".")},t.prototype._forEachChild=function(n){var t=this;Object.keys(this.controls).forEach(function(e){return n(t.controls[e],e)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){t.setParent(n),t._registerOnCollectionChange(n._onCollectionChange)})},t.prototype._updateValue=function(){this.value=this._reduceValue()},t.prototype._anyControls=function(n){var t=this,e=!1;return this._forEachChild(function(r,o){e=e||t.contains(o)&&n(r)}),e},t.prototype._reduceValue=function(){var n=this;return this._reduceChildren({},function(t,e,r){return(e.enabled||n.disabled)&&(t[r]=e.value),t})},t.prototype._reduceChildren=function(n,t){var e=n;return this._forEachChild(function(n,r){e=t(e,n,r)}),e},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(Object.keys(this.controls)),r=e.next();!r.done;r=e.next())if(this.controls[r.value].enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return Object.keys(this.controls).length>0||this.disabled},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control with name: '"+e+"'.")})},t}(eh),ih=function(n){function t(t,e,r){var o=n.call(this,Xf(e),nh(r,e))||this;return o.controls=t,o._initObservables(),o._setUpdateStrategy(e),o._setUpControls(),o.updateValueAndValidity({onlySelf:!0,emitEvent:!1}),o}return o(t,n),t.prototype.at=function(n){return this.controls[n]},t.prototype.push=function(n){this.controls.push(n),this._registerControl(n),this.updateValueAndValidity(),this._onCollectionChange()},t.prototype.insert=function(n,t){this.controls.splice(n,0,t),this._registerControl(t),this.updateValueAndValidity()},t.prototype.removeAt=function(n){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),this.updateValueAndValidity()},t.prototype.setControl=function(n,t){this.controls[n]&&this.controls[n]._registerOnCollectionChange(function(){}),this.controls.splice(n,1),t&&(this.controls.splice(n,0,t),this._registerControl(t)),this.updateValueAndValidity(),this._onCollectionChange()},Object.defineProperty(t.prototype,"length",{get:function(){return this.controls.length},enumerable:!0,configurable:!0}),t.prototype.setValue=function(n,t){var e=this;void 0===t&&(t={}),this._checkAllValuesPresent(n),n.forEach(function(n,r){e._throwIfControlMissing(r),e.at(r).setValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.patchValue=function(n,t){var e=this;void 0===t&&(t={}),n.forEach(function(n,r){e.at(r)&&e.at(r).patchValue(n,{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t)},t.prototype.reset=function(n,t){void 0===n&&(n=[]),void 0===t&&(t={}),this._forEachChild(function(e,r){e.reset(n[r],{onlySelf:!0,emitEvent:t.emitEvent})}),this.updateValueAndValidity(t),this._updatePristine(t),this._updateTouched(t)},t.prototype.getRawValue=function(){return this.controls.map(function(n){return n instanceof rh?n.value:n.getRawValue()})},t.prototype._syncPendingControls=function(){var n=this.controls.reduce(function(n,t){return!!t._syncPendingControls()||n},!1);return n&&this.updateValueAndValidity({onlySelf:!0}),n},t.prototype._throwIfControlMissing=function(n){if(!this.controls.length)throw new Error("\n There are no form controls registered with this array yet. If you're using ngModel,\n you may want to check next tick (e.g. use setTimeout).\n ");if(!this.at(n))throw new Error("Cannot find form control at index "+n)},t.prototype._forEachChild=function(n){this.controls.forEach(function(t,e){n(t,e)})},t.prototype._updateValue=function(){var n=this;this.value=this.controls.filter(function(t){return t.enabled||n.disabled}).map(function(n){return n.value})},t.prototype._anyControls=function(n){return this.controls.some(function(t){return t.enabled&&n(t)})},t.prototype._setUpControls=function(){var n=this;this._forEachChild(function(t){return n._registerControl(t)})},t.prototype._checkAllValuesPresent=function(n){this._forEachChild(function(t,e){if(void 0===n[e])throw new Error("Must supply a value for form control at index: "+e+".")})},t.prototype._allControlsDisabled=function(){var n,t;try{for(var e=s(this.controls),r=e.next();!r.done;r=e.next())if(r.value.enabled)return!1}catch(o){n={error:o}}finally{try{r&&!r.done&&(t=e.return)&&t.call(e)}finally{if(n)throw n.error}}return this.controls.length>0||this.disabled},t.prototype._registerControl=function(n){n.setParent(this),n._registerOnCollectionChange(this._onCollectionChange)},t}(eh),lh=Promise.resolve(null),uh=function(n){function t(t,e){var r=n.call(this)||this;return r.submitted=!1,r._directives=[],r.ngSubmit=new Ho,r.form=new oh({},Qf(t),Wf(e)),r}return o(t,n),t.prototype.ngAfterViewInit=function(){this._setUpdateStrategy()},Object.defineProperty(t.prototype,"formDirective",{get:function(){return this},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"control",{get:function(){return this.form},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"path",{get:function(){return[]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"controls",{get:function(){return this.form.controls},enumerable:!0,configurable:!0}),t.prototype.addControl=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);n.control=e.registerControl(n.name,n.control),Zf(n.control,n),n.control.updateValueAndValidity({emitEvent:!1}),t._directives.push(n)})},t.prototype.getControl=function(n){return this.form.get(n.path)},t.prototype.removeControl=function(n){var t=this;lh.then(function(){var e,r,o=t._findContainer(n.path);o&&o.removeControl(n.name),(r=(e=t._directives).indexOf(n))>-1&&e.splice(r,1)})},t.prototype.addFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path),r=new oh({});(function(n,t){null==n&&$f(t,"Cannot find control with"),n.validator=_f.compose([n.validator,t.validator]),n.asyncValidator=_f.composeAsync([n.asyncValidator,t.asyncValidator])})(r,n),e.registerControl(n.name,r),r.updateValueAndValidity({emitEvent:!1})})},t.prototype.removeFormGroup=function(n){var t=this;lh.then(function(){var e=t._findContainer(n.path);e&&e.removeControl(n.name)})},t.prototype.getFormGroup=function(n){return this.form.get(n.path)},t.prototype.updateModel=function(n,t){var e=this;lh.then(function(){e.form.get(n.path).setValue(t)})},t.prototype.setValue=function(n){this.control.setValue(n)},t.prototype.onSubmit=function(n){return this.submitted=!0,t=this._directives,this.form._syncPendingControls(),t.forEach(function(n){var t=n.control;"submit"===t.updateOn&&t._pendingChange&&(n.viewToModelUpdate(t._pendingValue),t._pendingChange=!1)}),this.ngSubmit.emit(n),!1;var t},t.prototype.onReset=function(){this.resetForm()},t.prototype.resetForm=function(n){void 0===n&&(n=void 0),this.form.reset(n),this.submitted=!1},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.form._updateOn=this.options.updateOn)},t.prototype._findContainer=function(n){return n.pop(),n.length?this.form.get(n):this.form},t}(yf),sh=function(){function n(){}return n.modelParentException=function(){throw new Error('\n ngModel cannot be used to register form controls with a parent formGroup directive. Try using\n formGroup\'s partner directive "formControlName" instead. Example:\n\n \n
\n \n
\n\n In your class:\n\n this.myGroup = new FormGroup({\n firstName: new FormControl()\n });\n\n Or, if you\'d like to avoid registering this form control, indicate that it\'s standalone in ngModelOptions:\n\n Example:\n\n \n
\n \n \n
\n ')},n.formGroupNameException=function(){throw new Error("\n ngModel cannot be used to register form controls with a parent formGroupName or formArrayName directive.\n\n Option 1: Use formControlName instead of ngModel (reactive strategy):\n\n "+Hf+"\n\n Option 2: Update ngModel's parent be ngModelGroup (template-driven strategy):\n\n "+Rf)},n.missingNameException=function(){throw new Error('If ngModel is used within a form tag, either the name attribute must be set or the form\n control must be defined as \'standalone\' in ngModelOptions.\n\n Example 1: \n Example 2: ')},n.modelGroupParentException=function(){throw new Error("\n ngModelGroup cannot be used with a parent formGroup directive.\n\n Option 1: Use formGroupName instead of ngModelGroup (reactive strategy):\n\n "+Hf+"\n\n Option 2: Use a regular form tag instead of the formGroup directive (template-driven strategy):\n\n "+Rf)},n.ngFormWarning=function(){console.warn("\n It looks like you're using 'ngForm'.\n\n Support for using the 'ngForm' element selector has been deprecated in Angular v6 and will be removed\n in Angular v9.\n\n Use 'ng-form' instead.\n\n Before:\n \n\n After:\n \n ")},n}(),ah=new wn("NgFormSelectorWarning"),ch=function(n){function t(t,e,r){var o=n.call(this)||this;return o._parent=t,o._validators=e,o._asyncValidators=r,o}var e;return o(t,n),e=t,t.prototype._checkParentType=function(){this._parent instanceof e||this._parent instanceof uh||sh.modelGroupParentException()},t}(Jf),fh=Promise.resolve(null),hh=function(n){function t(t,e,r,o){var i=n.call(this)||this;return i.control=new rh,i._registered=!1,i.update=new Ho,i._parent=t,i._rawValidators=e||[],i._rawAsyncValidators=r||[],i.valueAccessor=function(n,t){if(!t)return null;Array.isArray(t)||$f(n,"Value accessor was not provided as an array for form control with");var e=void 0,r=void 0,o=void 0;return t.forEach(function(t){var i;t.constructor===Sf?e=t:(i=t,Kf.some(function(n){return i.constructor===n})?(r&&$f(n,"More than one built-in value accessor matches form control with"),r=t):(o&&$f(n,"More than one custom value accessor matches form control with"),o=t))}),o||r||e||($f(n,"No valid value accessor for form control with"),null)}(i,o),i}return o(t,n),t.prototype.ngOnChanges=function(n){this._checkForErrors(),this._registered||this._setUpControl(),"isDisabled"in n&&this._updateDisabled(n),function(n,t){if(!n.hasOwnProperty("model"))return!1;var e=n.model;return!!e.isFirstChange()||!Nn(t,e.currentValue)}(n,this.viewModel)&&(this._updateValue(this.model),this.viewModel=this.model)},t.prototype.ngOnDestroy=function(){this.formDirective&&this.formDirective.removeControl(this)},Object.defineProperty(t.prototype,"path",{get:function(){return this._parent?Gf(this.name,this._parent):[this.name]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"formDirective",{get:function(){return this._parent?this._parent.formDirective:null},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"validator",{get:function(){return Qf(this._rawValidators)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"asyncValidator",{get:function(){return Wf(this._rawAsyncValidators)},enumerable:!0,configurable:!0}),t.prototype.viewToModelUpdate=function(n){this.viewModel=n,this.update.emit(n)},t.prototype._setUpControl=function(){this._setUpdateStrategy(),this._isStandalone()?this._setUpStandalone():this.formDirective.addControl(this),this._registered=!0},t.prototype._setUpdateStrategy=function(){this.options&&null!=this.options.updateOn&&(this.control._updateOn=this.options.updateOn)},t.prototype._isStandalone=function(){return!this._parent||!(!this.options||!this.options.standalone)},t.prototype._setUpStandalone=function(){Zf(this.control,this),this.control.updateValueAndValidity({emitEvent:!1})},t.prototype._checkForErrors=function(){this._isStandalone()||this._checkParentType(),this._checkName()},t.prototype._checkParentType=function(){!(this._parent instanceof ch)&&this._parent instanceof Jf?sh.formGroupNameException():this._parent instanceof ch||this._parent instanceof uh||sh.modelParentException()},t.prototype._checkName=function(){this.options&&this.options.name&&(this.name=this.options.name),this._isStandalone()||this.name||sh.missingNameException()},t.prototype._updateValue=function(n){var t=this;fh.then(function(){t.control.setValue(n,{emitViewToModelChange:!1})})},t.prototype._updateDisabled=function(n){var t=this,e=n.isDisabled.currentValue,r=""===e||e&&"false"!==e;fh.then(function(){r&&!t.control.disabled?t.control.disable():!r&&t.control.disabled&&t.control.enable()})},t}(Nf),ph=function(){return function(){}}(),dh=function(){function n(){}var t;return t=n,n.withConfig=function(n){return{ngModule:t,providers:[{provide:ah,useValue:n.warnOnDeprecatedNgFormSelector}]}},n}(),gh=Pl({encapsulation:2,styles:[],data:{}});function vh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function yh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","20"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["20"]))],function(n,t){n(t,1,0,"20"),n(t,2,0,"20")},null)}function mh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","50"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["50"]))],function(n,t){n(t,1,0,"50"),n(t,2,0,"50")},null)}function bh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","100"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["100"]))],function(n,t){n(t,1,0,"100"),n(t,2,0,"100")},null)}function _h(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){var e=t.component;n(t,1,0,e.totalNumberOfRiskHotspots),n(t,2,0,e.totalNumberOfRiskHotspots)},function(n,t){n(t,3,0,t.component.translations.all)})}function wh(n){return bs(0,[(n()(),lu(0,0,null,null,17,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,1).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,1).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.numberOfRiskHotspots=e)&&r),r},null,null)),Qu(1,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(3,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(5,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(6,0,null,null,3,"option",[["value","10"]],null,null,null,null,null)),Qu(7,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(8,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(-1,null,["10"])),(n()(),iu(16777216,null,null,1,null,yh)),Qu(11,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mh)),Qu(13,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bh)),Qu(15,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,_h)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,3,0,e.settings.numberOfRiskHotspots),n(t,7,0,"10"),n(t,8,0,"10"),n(t,11,0,e.totalNumberOfRiskHotspots>10),n(t,13,0,e.totalNumberOfRiskHotspots>20),n(t,15,0,e.totalNumberOfRiskHotspots>50),n(t,17,0,e.totalNumberOfRiskHotspots>100)},function(n,t){n(t,0,0,Vu(t,5).ngClassUntouched,Vu(t,5).ngClassTouched,Vu(t,5).ngClassPristine,Vu(t,5).ngClassDirty,Vu(t,5).ngClassValid,Vu(t,5).ngClassInvalid,Vu(t,5).ngClassPending)})}function Ch(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null))],null,null)}function Eh(n){return bs(0,[(n()(),lu(0,0,null,null,7,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting(""+n.context.index,e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),lu(7,0,null,null,0,"i",[["class","icon-info-circled"]],null,null,null,null,null))],function(n,t){var e=t.component,r=n(t,4,0,e.settings.sortBy===""+t.context.index&&"desc"===e.settings.sortOrder,e.settings.sortBy===""+t.context.index&&"asc"===e.settings.sortOrder,e.settings.sortBy!==""+t.context.index);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.context.$implicit.name),n(t,6,0,ru(1,"",t.context.$implicit.explanationUrl,""))})}function xh(n){return bs(0,[(n()(),lu(0,0,null,null,3,"td",[["class","right"]],null,null,null,null,null)),Qu(1,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(2,{lightred:0,lightgreen:1}),(n()(),vs(3,null,["",""]))],function(n,t){var e=n(t,2,0,t.context.$implicit.exceeded,!t.context.$implicit.exceeded);n(t,1,0,"right",e)},function(n,t){n(t,3,0,t.context.$implicit.value)})}function Oh(n){return bs(0,[(n()(),lu(0,0,null,null,10,"tr",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"td",[],null,null,null,null,null)),(n()(),vs(2,null,["",""])),(n()(),lu(3,0,null,null,2,"td",[],null,null,null,null,null)),(n()(),lu(4,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(5,null,["",""])),(n()(),lu(6,0,null,null,2,"td",[],[[8,"title",0]],null,null,null,null)),(n()(),lu(7,0,null,null,1,"a",[],[[8,"href",4]],null,null,null,null)),(n()(),vs(8,null,[" "," "])),(n()(),iu(16777216,null,null,1,null,xh)),Qu(10,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){n(t,10,0,t.context.$implicit.metrics)},function(n,t){n(t,2,0,t.context.$implicit.assembly),n(t,4,0,t.context.$implicit.reportPath),n(t,5,0,t.context.$implicit.class),n(t,6,0,t.context.$implicit.methodName),n(t,7,0,t.context.$implicit.reportPath+"#file"+t.context.$implicit.fileIndex+"_line"+t.context.$implicit.line),n(t,8,0,t.context.$implicit.methodShortName)})}function kh(n){return bs(0,[(n()(),lu(0,0,null,null,62,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,28,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,12,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,11,"select",[["name","assembly"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.assembly=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{name:[0,"name"],model:[1,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,vh)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(15,0,null,null,4,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(16,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(17,null,["",""])),(n()(),iu(16777216,null,null,1,null,wh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,0,"div",[["class","center"]],null,null,null,null,null)),(n()(),lu(21,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(22,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(23,null,[""," "])),(n()(),lu(24,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,25)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,25).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,25)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,25)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateRiskHotpots()&&r),r},null,null)),Qu(25,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(27,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(29,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(30,0,null,null,32,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(31,0,null,null,5,"colgroup",[],null,null,null,null,null)),(n()(),lu(32,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(33,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(34,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ch)),Qu(36,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(37,0,null,null,21,"thead",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,20,"tr",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(40,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("assembly",e)&&r),r},null,null)),(n()(),lu(41,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(42,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(43,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(44,null,["",""])),(n()(),lu(45,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(46,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("class",e)&&r),r},null,null)),(n()(),lu(47,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(48,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(49,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(50,null,["",""])),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("method",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),iu(16777216,null,null,1,null,Eh)),Qu(58,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),lu(59,0,null,null,3,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,2,null,Oh)),Qu(61,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(t=0,e=ec,r=[],Ku(-1,t|=16,null,0,e,e,r))],function(n,t){var e=t.component;n(t,6,0,"assembly",e.settings.assembly),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.assemblies),n(t,19,0,e.totalNumberOfRiskHotspots>10),n(t,27,0,e.settings.filter),n(t,36,0,e.riskHotspotMetrics);var r=n(t,43,0,"assembly"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"assembly"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"assembly"!==e.settings.sortBy);n(t,42,0,"icon-down-dir",r);var o=n(t,49,0,"class"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"class"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"class"!==e.settings.sortBy);n(t,48,0,"icon-down-dir",o);var i=n(t,55,0,"method"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"method"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"method"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",i),n(t,58,0,e.riskHotspotMetrics),n(t,61,0,function(n,t,e,r){if(_t.isWrapped(r)){r=_t.unwrap(r);var o=n.def.nodes[61].bindingIndex+0,i=_t.unwrap(n.oldValues[o]);n.oldValues[o]=new _t(i)}return r}(t,0,0,Vu(t,62).transform(e.riskHotspots,0,e.settings.numberOfRiskHotspots)))},function(n,t){var e=t.component;n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.assembly),n(t,17,0,e.translations.top),n(t,23,0,e.translations.filter),n(t,24,0,Vu(t,29).ngClassUntouched,Vu(t,29).ngClassTouched,Vu(t,29).ngClassPristine,Vu(t,29).ngClassDirty,Vu(t,29).ngClassValid,Vu(t,29).ngClassInvalid,Vu(t,29).ngClassPending),n(t,44,0,e.translations.assembly),n(t,50,0,e.translations.class),n(t,56,0,e.translations.method)});var t,e,r}function Sh(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,kh)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.totalNumberOfRiskHotspots>0)},null)}function Ah(n){return bs(0,[(n()(),lu(0,0,null,null,1,"risk-hotspots",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Sh,gh)),Qu(1,114688,null,0,Na,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Th=xu("risk-hotspots",Na,Ah,{},{},[]),Ih=function(){function n(){this.grayVisible=!0,this.greenVisible=!1,this.redVisible=!1,this.greenClass="",this.redClass="",this._percentage=NaN}return Object.defineProperty(n.prototype,"percentage",{get:function(){return this._percentage},set:function(n){this._percentage=n,this.grayVisible=isNaN(n),this.greenVisible=!isNaN(n)&&Math.round(n)>0,this.redVisible=!isNaN(n)&&100-Math.round(n)>0,this.greenClass="covered"+Math.round(n),this.redClass="covered"+(100-Math.round(n))},enumerable:!0,configurable:!0}),n}(),Ph=Pl({encapsulation:2,styles:[],data:{}});function Nh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[["class","gray covered100"]],null,null,null,null,null))],null,null)}function Dh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"green ",t.component.greenClass,""))})}function Mh(n){return bs(0,[(n()(),lu(0,0,null,null,0,"td",[],[[8,"className",0]],null,null,null,null))],null,function(n,t){n(t,0,0,ru(1,"red ",t.component.redClass,""))})}function Vh(n){return bs(2,[(n()(),lu(0,0,null,null,6,"table",[["class","coverage"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Nh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Dh)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Mh)),Qu(6,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,e.grayVisible),n(t,4,0,e.greenVisible),n(t,6,0,e.redVisible)},null)}var Hh=function(){return function(){this.element=null,this.collapsed=!1,this.branchCoverageAvailable=!1}}(),Rh=Pl({encapsulation:2,styles:[],data:{}});function jh(n){return bs(0,[(n()(),lu(0,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.element.branchCoveragePercentage)})}function Lh(n){return bs(0,[(n()(),lu(0,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.element.branchCoverage)},null)}function zh(n){return bs(2,[(n()(),lu(0,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.element.toggleCollapse(e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{ngClass:[0,"ngClass"]},null),gs(4,{"icon-plus":0,"icon-minus":1}),(n()(),vs(5,null,[" ",""])),(n()(),lu(6,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(9,null,["",""])),(n()(),lu(10,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(11,null,["",""])),(n()(),lu(12,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(13,null,["",""])),(n()(),lu(14,0,null,null,1,"th",[["class","right"]],null,null,null,null,null)),(n()(),vs(15,null,["",""])),(n()(),lu(16,0,null,null,2,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(17,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(18,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,jh)),Qu(20,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Lh)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component,r=n(t,4,0,e.element.collapsed,!e.element.collapsed);n(t,3,0,r),n(t,18,0,e.element.coverage),n(t,20,0,e.branchCoverageAvailable),n(t,22,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,5,0,e.element.name),n(t,7,0,e.element.coveredLines),n(t,9,0,e.element.uncoveredLines),n(t,11,0,e.element.coverableLines),n(t,13,0,e.element.totalLines),n(t,15,0,e.element.coveragePercentage)})}var Fh=function(){function n(){this.path=null,this._historicCoverages=[]}return Object.defineProperty(n.prototype,"historicCoverages",{get:function(){return this._historicCoverages},set:function(n){if(this._historicCoverages=n,n.length>1){for(var t="",e=0;et?"lightgreen":n1),n(t,4,0,null!==e.clazz.currentHistoricCoverage),n(t,6,0,null===e.clazz.currentHistoricCoverage)},null)}function ap(n){return bs(0,[(n()(),lu(0,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(2,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null)],function(n,t){n(t,2,0,t.component.clazz.branchCoverage)},null)}function cp(n){return bs(2,[(n()(),lu(0,0,null,null,4,"td",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,qh)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,$h)),Qu(4,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(5,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Qh)),Qu(7,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Wh)),Qu(9,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(10,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Kh)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Jh)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Yh)),Qu(17,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Xh)),Qu(19,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(20,0,null,null,4,"td",[["class","right"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,np)),Qu(22,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,tp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(25,0,null,null,6,"td",[["class","right"]],[[8,"title",0]],null,null,null,null)),(n()(),iu(16777216,null,null,1,null,ep)),Qu(27,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,rp)),Qu(29,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,op)),Qu(31,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(32,0,null,null,2,"td",[["class","right"]],null,null,null,null,null)),(n()(),lu(33,0,null,null,1,"coverage-bar",[],null,null,null,Vh,Ph)),Qu(34,49152,null,0,Ih,[],{percentage:[0,"percentage"]},null),(n()(),iu(16777216,null,null,1,null,sp)),Qu(36,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,ap)),Qu(38,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,2,0,""!==e.clazz.reportPath),n(t,4,0,""===e.clazz.reportPath),n(t,7,0,null!==e.clazz.currentHistoricCoverage),n(t,9,0,null===e.clazz.currentHistoricCoverage),n(t,12,0,null!==e.clazz.currentHistoricCoverage),n(t,14,0,null===e.clazz.currentHistoricCoverage),n(t,17,0,null!==e.clazz.currentHistoricCoverage),n(t,19,0,null===e.clazz.currentHistoricCoverage),n(t,22,0,null!==e.clazz.currentHistoricCoverage),n(t,24,0,null===e.clazz.currentHistoricCoverage),n(t,27,0,e.clazz.lineCoverageHistory.length>1),n(t,29,0,null!==e.clazz.currentHistoricCoverage),n(t,31,0,null===e.clazz.currentHistoricCoverage),n(t,34,0,e.clazz.coverage),n(t,36,0,e.branchCoverageAvailable),n(t,38,0,e.branchCoverageAvailable)},function(n,t){n(t,25,0,t.component.clazz.coverageType)})}var fp=Pl({encapsulation:2,styles:[],data:{}});function hp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.noGrouping)})}function pp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){n(t,1,0,t.component.translations.byAssembly)})}function dp(n){return bs(0,[(n()(),lu(0,0,null,null,1,null,null,null,null,null,null,null)),(n()(),vs(1,null,["",""]))],null,function(n,t){var e=t.component;n(t,1,0,e.translations.byNamespace+" "+e.settings.grouping)})}function gp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,["",""]))],function(n,t){n(t,1,0,t.context.$implicit),n(t,2,0,t.context.$implicit)},function(n,t){n(t,3,0,t.context.$implicit)})}function vp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"br",[],null,null,null,null,null))],null,null)}function yp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageIncreaseOnly"),n(t,2,0,"branchCoverageIncreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageIncreaseOnly)})}function mp(n){return bs(0,[(n()(),lu(0,0,null,null,3,"option",[["value","branchCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(1,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(2,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(3,null,[" "," "]))],function(n,t){n(t,1,0,"branchCoverageDecreaseOnly"),n(t,2,0,"branchCoverageDecreaseOnly")},function(n,t){n(t,3,0,t.component.translations.branchCoverageDecreaseOnly)})}function bp(n){return bs(0,[(n()(),lu(0,0,null,null,26,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,25,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,2).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,2).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionType=e)&&r),r},null,null)),Qu(2,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(4,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(6,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(7,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(8,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(9,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(10,null,["",""])),(n()(),lu(11,0,null,null,3,"option",[["value","allChanges"]],null,null,null,null,null)),Qu(12,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(13,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(14,null,["",""])),(n()(),lu(15,0,null,null,3,"option",[["value","lineCoverageIncreaseOnly"]],null,null,null,null,null)),Qu(16,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(17,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(18,null,["",""])),(n()(),lu(19,0,null,null,3,"option",[["value","lineCoverageDecreaseOnly"]],null,null,null,null,null)),Qu(20,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(21,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(22,null,["",""])),(n()(),iu(16777216,null,null,1,null,yp)),Qu(24,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,mp)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){var e=t.component;n(t,4,0,e.settings.historyComparisionType),n(t,8,0,""),n(t,9,0,""),n(t,12,0,"allChanges"),n(t,13,0,"allChanges"),n(t,16,0,"lineCoverageIncreaseOnly"),n(t,17,0,"lineCoverageIncreaseOnly"),n(t,20,0,"lineCoverageDecreaseOnly"),n(t,21,0,"lineCoverageDecreaseOnly"),n(t,24,0,e.branchCoverageAvailable),n(t,26,0,e.branchCoverageAvailable)},function(n,t){var e=t.component;n(t,1,0,Vu(t,6).ngClassUntouched,Vu(t,6).ngClassTouched,Vu(t,6).ngClassPristine,Vu(t,6).ngClassDirty,Vu(t,6).ngClassValid,Vu(t,6).ngClassInvalid,Vu(t,6).ngClassPending),n(t,10,0,e.translations.filter),n(t,14,0,e.translations.allChanges),n(t,18,0,e.translations.lineCoverageIncreaseOnly),n(t,22,0,e.translations.lineCoverageDecreaseOnly)})}function _p(n){return bs(0,[(n()(),lu(0,0,null,null,18,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,13,"div",[],null,null,null,null,null)),(n()(),vs(2,null,[" "," "])),(n()(),lu(3,0,null,null,11,"select",[],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"change"],[null,"blur"]],function(n,t,e){var r=!0,o=n.component;return"change"===t&&(r=!1!==Vu(n,4).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,4).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.historyComparisionDate=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCurrentHistoricCoverage()&&r),r},null,null)),Qu(4,16384,null,0,Lf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n){return[n]},[Lf]),Qu(6,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(8,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(9,0,null,null,3,"option",[["value",""]],null,null,null,null,null)),Qu(10,147456,null,0,zf,[Qr,Xr,[2,Lf]],{value:[0,"value"]},null),Qu(11,147456,null,0,Uf,[Qr,Xr,[8,null]],{value:[0,"value"]},null),(n()(),vs(12,null,["",""])),(n()(),iu(16777216,null,null,1,null,gp)),Qu(14,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,vp)),Qu(16,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,bp)),Qu(18,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component;n(t,6,0,e.settings.historyComparisionDate),n(t,10,0,""),n(t,11,0,""),n(t,14,0,e.historicCoverageExecutionTimes),n(t,16,0,""!==e.settings.historyComparisionDate),n(t,18,0,""!==e.settings.historyComparisionDate)},function(n,t){var e=t.component;n(t,2,0,e.translations.compareHistory),n(t,3,0,Vu(t,8).ngClassUntouched,Vu(t,8).ngClassTouched,Vu(t,8).ngClassPristine,Vu(t,8).ngClassDirty,Vu(t,8).ngClassValid,Vu(t,8).ngClassInvalid,Vu(t,8).ngClassPending),n(t,12,0,e.translations.date)})}function wp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null))],null,null)}function Cp(n){return bs(0,[(n()(),lu(0,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null))],null,null)}function Ep(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"coverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.coverage)})}function xp(n){return bs(0,[(n()(),lu(0,0,null,null,5,"th",[["class","center"],["colspan","2"]],null,null,null,null,null)),(n()(),lu(1,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("branchcoverage",e)&&r),r},null,null)),(n()(),lu(2,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(3,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(4,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(5,null,["",""]))],function(n,t){var e=t.component,r=n(t,4,0,"branchcoverage"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"branchcoverage"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"branchcoverage"!==e.settings.sortBy);n(t,3,0,"icon-down-dir",r)},function(n,t){n(t,5,0,t.component.translations.branchCoverage)})}function Op(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["codeelement-row",""]],null,null,null,zh,Rh)),Qu(1,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null)],function(n,t){n(t,1,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable)},null)}function kp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Sp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,kp)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ap(n){return bs(0,[(n()(),lu(0,0,null,null,1,"tr",[["class","namespace"],["class-row",""]],null,null,null,cp,Zh)),Qu(1,49152,null,0,Gh,[],{clazz:[0,"clazz"],translations:[1,"translations"],branchCoverageAvailable:[2,"branchCoverageAvailable"],historyComparisionDate:[3,"historyComparisionDate"]},null)],function(n,t){var e=t.component;n(t,1,0,t.parent.context.$implicit,e.translations,e.branchCoverageAvailable,e.settings.historyComparisionDate)},null)}function Tp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ap)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Ip(n){return bs(0,[(n()(),lu(0,0,null,null,4,null,null,null,null,null,null,null)),(n()(),lu(1,0,null,null,1,"tr",[["class","namespace"],["codeelement-row",""]],null,null,null,zh,Rh)),Qu(2,49152,null,0,Hh,[],{element:[0,"element"],collapsed:[1,"collapsed"],branchCoverageAvailable:[2,"branchCoverageAvailable"]},null),(n()(),iu(16777216,null,null,1,null,Tp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){n(t,2,0,t.parent.context.$implicit,t.parent.context.$implicit.collapsed,t.component.branchCoverageAvailable),n(t,4,0,t.parent.context.$implicit.classes)},null)}function Pp(n){return bs(0,[(n()(),lu(0,0,null,null,2,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Ip)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=!t.parent.context.$implicit.collapsed&&t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r)},null)}function Np(n){return bs(0,[(n()(),lu(0,0,null,null,6,null,null,null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Op)),Qu(2,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Sp)),Qu(4,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(16777216,null,null,1,null,Pp)),Qu(6,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null),(n()(),iu(0,null,null,0))],function(n,t){var e=t.component,r=t.context.$implicit.visible(e.settings.filter,e.settings.historyComparisionType);n(t,2,0,r),n(t,4,0,t.context.$implicit.classes),n(t,6,0,t.context.$implicit.subElements)},null)}function Dp(n){return bs(0,[(n()(),lu(0,0,null,null,87,"div",[],null,null,null,null,null)),(n()(),lu(1,0,null,null,34,"div",[["class","customizebox"]],null,null,null,null,null)),(n()(),lu(2,0,null,null,5,"div",[],null,null,null,null,null)),(n()(),lu(3,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.collapseAll(e)&&r),r},null,null)),(n()(),vs(4,null,["",""])),(n()(),vs(-1,null,[" | "])),(n()(),lu(6,0,null,null,1,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.expandAll(e)&&r),r},null,null)),(n()(),vs(7,null,["",""])),(n()(),lu(8,0,null,null,15,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,hp)),Qu(10,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,pp)),Qu(12,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,dp)),Qu(14,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(15,0,null,null,0,"br",[],null,null,null,null,null)),(n()(),vs(16,null,[" "," "])),(n()(),lu(17,0,null,null,6,"input",[["min","-1"],["step","1"],["type","range"]],[[8,"max",0],[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"],[null,"change"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,18)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,18).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,18)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,18)._compositionEnd(e.target.value)&&r),"change"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"input"===t&&(r=!1!==Vu(n,19).onChange(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,19).onTouched()&&r),"ngModelChange"===t&&(r=!1!==(o.settings.grouping=e)&&r),"ngModelChange"===t&&(r=!1!==o.updateCoverageInfo()&&r),r},null,null)),Qu(18,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Qu(19,16384,null,0,Vf,[Xr,Qr],null,null),Wu(1024,null,xf,function(n,t){return[n,t]},[Sf,Vf]),Qu(21,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(23,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(24,0,null,null,2,"div",[["class","center"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,_p)),Qu(26,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(27,0,null,null,8,"div",[["class","right"]],null,null,null,null,null)),(n()(),lu(28,0,null,null,1,"span",[],null,null,null,null,null)),(n()(),vs(29,null,[""," "])),(n()(),lu(30,0,null,null,5,"input",[["type","text"]],[[2,"ng-untouched",null],[2,"ng-touched",null],[2,"ng-pristine",null],[2,"ng-dirty",null],[2,"ng-valid",null],[2,"ng-invalid",null],[2,"ng-pending",null]],[[null,"ngModelChange"],[null,"input"],[null,"blur"],[null,"compositionstart"],[null,"compositionend"]],function(n,t,e){var r=!0,o=n.component;return"input"===t&&(r=!1!==Vu(n,31)._handleInput(e.target.value)&&r),"blur"===t&&(r=!1!==Vu(n,31).onTouched()&&r),"compositionstart"===t&&(r=!1!==Vu(n,31)._compositionStart()&&r),"compositionend"===t&&(r=!1!==Vu(n,31)._compositionEnd(e.target.value)&&r),"ngModelChange"===t&&(r=!1!==(o.settings.filter=e)&&r),r},null,null)),Qu(31,16384,null,0,Sf,[Xr,Qr,[2,kf]],null,null),Wu(1024,null,xf,function(n){return[n]},[Sf]),Qu(33,671744,null,0,hh,[[8,null],[8,null],[8,null],[6,xf]],{model:[0,"model"]},{update:"ngModelChange"}),Wu(2048,null,Nf,null,[hh]),Qu(35,16384,null,0,Yf,[[4,Nf]],null,null),(n()(),lu(36,0,null,null,51,"table",[["class","overview table-fixed stripped"]],null,null,null,null,null)),(n()(),lu(37,0,null,null,11,"colgroup",[],null,null,null,null,null)),(n()(),lu(38,0,null,null,0,"col",[],null,null,null,null,null)),(n()(),lu(39,0,null,null,0,"col",[["class","column90"]],null,null,null,null,null)),(n()(),lu(40,0,null,null,0,"col",[["class","column105"]],null,null,null,null,null)),(n()(),lu(41,0,null,null,0,"col",[["class","column100"]],null,null,null,null,null)),(n()(),lu(42,0,null,null,0,"col",[["class","column70"]],null,null,null,null,null)),(n()(),lu(43,0,null,null,0,"col",[["class","column98"]],null,null,null,null,null)),(n()(),lu(44,0,null,null,0,"col",[["class","column112"]],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,wp)),Qu(46,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,Cp)),Qu(48,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(49,0,null,null,35,"thead",[],null,null,null,null,null)),(n()(),lu(50,0,null,null,34,"tr",[],null,null,null,null,null)),(n()(),lu(51,0,null,null,5,"th",[],null,null,null,null,null)),(n()(),lu(52,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("name",e)&&r),r},null,null)),(n()(),lu(53,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(54,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(55,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(56,null,["",""])),(n()(),lu(57,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(58,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("covered",e)&&r),r},null,null)),(n()(),lu(59,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(60,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(61,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(62,null,["",""])),(n()(),lu(63,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(64,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("uncovered",e)&&r),r},null,null)),(n()(),lu(65,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(66,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(67,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(68,null,["",""])),(n()(),lu(69,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(70,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("coverable",e)&&r),r},null,null)),(n()(),lu(71,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(72,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(73,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(74,null,["",""])),(n()(),lu(75,0,null,null,5,"th",[["class","right"]],null,null,null,null,null)),(n()(),lu(76,0,null,null,4,"a",[["href","#"]],null,[[null,"click"]],function(n,t,e){var r=!0;return"click"===t&&(r=!1!==n.component.updateSorting("total",e)&&r),r},null,null)),(n()(),lu(77,0,null,null,2,"i",[["class","icon-down-dir"]],null,null,null,null,null)),Qu(78,278528,null,0,Wa,[ol,il,Qr,Xr],{klass:[0,"klass"],ngClass:[1,"ngClass"]},null),gs(79,{"icon-up-dir_active":0,"icon-down-dir_active":1,"icon-down-dir":2}),(n()(),vs(80,null,["",""])),(n()(),iu(16777216,null,null,1,null,Ep)),Qu(82,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),iu(16777216,null,null,1,null,xp)),Qu(84,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null),(n()(),lu(85,0,null,null,2,"tbody",[],null,null,null,null,null)),(n()(),iu(16777216,null,null,1,null,Np)),Qu(87,278528,null,0,Ja,[ji,Ro,ol],{ngForOf:[0,"ngForOf"]},null)],function(n,t){var e=t.component;n(t,10,0,-1===e.settings.grouping),n(t,12,0,0===e.settings.grouping),n(t,14,0,e.settings.grouping>0),n(t,21,0,e.settings.grouping),n(t,26,0,e.historicCoverageExecutionTimes.length>0),n(t,33,0,e.settings.filter),n(t,46,0,e.branchCoverageAvailable),n(t,48,0,e.branchCoverageAvailable);var r=n(t,55,0,"name"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"name"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"name"!==e.settings.sortBy);n(t,54,0,"icon-down-dir",r);var o=n(t,61,0,"covered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"covered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"covered"!==e.settings.sortBy);n(t,60,0,"icon-down-dir",o);var i=n(t,67,0,"uncovered"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"uncovered"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"uncovered"!==e.settings.sortBy);n(t,66,0,"icon-down-dir",i);var l=n(t,73,0,"coverable"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"coverable"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"coverable"!==e.settings.sortBy);n(t,72,0,"icon-down-dir",l);var u=n(t,79,0,"total"===e.settings.sortBy&&"desc"===e.settings.sortOrder,"total"===e.settings.sortBy&&"asc"===e.settings.sortOrder,"total"!==e.settings.sortBy);n(t,78,0,"icon-down-dir",u),n(t,82,0,e.branchCoverageAvailable),n(t,84,0,e.branchCoverageAvailable),n(t,87,0,e.codeElements)},function(n,t){var e=t.component;n(t,4,0,e.translations.collapseAll),n(t,7,0,e.translations.expandAll),n(t,16,0,e.translations.grouping),n(t,17,0,e.settings.groupingMaximum,Vu(t,23).ngClassUntouched,Vu(t,23).ngClassTouched,Vu(t,23).ngClassPristine,Vu(t,23).ngClassDirty,Vu(t,23).ngClassValid,Vu(t,23).ngClassInvalid,Vu(t,23).ngClassPending),n(t,29,0,e.translations.filter),n(t,30,0,Vu(t,35).ngClassUntouched,Vu(t,35).ngClassTouched,Vu(t,35).ngClassPristine,Vu(t,35).ngClassDirty,Vu(t,35).ngClassValid,Vu(t,35).ngClassInvalid,Vu(t,35).ngClassPending),n(t,56,0,e.translations.name),n(t,62,0,e.translations.covered),n(t,68,0,e.translations.uncovered),n(t,74,0,e.translations.coverable),n(t,80,0,e.translations.total)})}function Mp(n){return bs(0,[(n()(),iu(16777216,null,null,1,null,Dp)),Qu(1,16384,null,0,Xa,[ji,Ro],{ngIf:[0,"ngIf"]},null)],function(n,t){n(t,1,0,t.component.codeElements.length>0)},null)}function Vp(n){return bs(0,[(n()(),lu(0,0,null,null,1,"coverage-info",[],null,[["window","beforeunload"]],function(n,t,e){var r=!0;return"window:beforeunload"===t&&(r=!1!==Vu(n,1).onDonBeforeUnlodad()&&r),r},Mp,fp)),Qu(1,114688,null,0,ja,[Ta],null,null)],function(n,t){n(t,1,0)},null)}var Hp=xu("coverage-info",ja,Vp,{},{},[]),Rp=ka(Aa,[Na,ja],function(n){return function(n){for(var t={},e=[],r=!1,o=0;odiv { width: 25%; display: inline-block; } -.customizebox div.right input { font-size: 0.8em; width: 150px; } -#namespaceslider { width: 200px; display: inline-block; margin-left: 8px; } - -.percentagebarundefined { - border-left: 2px solid #fff; - padding-left: 3px; -} -.percentagebar0 { - border-left: 2px solid #c10909; - padding-left: 3px; -} -.percentagebar10 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 90%, #0aad0a 90%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar20 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 80%, #0aad0a 80%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar30 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 70%, #0aad0a 70%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar40 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 60%, #0aad0a 60%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar50 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 50%, #0aad0a 50%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar60 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 40%, #0aad0a 40%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar70 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 30%, #0aad0a 30%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar80 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 20%, #0aad0a 20%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar90 { - border-left: 2px solid; - border-image: linear-gradient(to bottom, #c10909 10%, #0aad0a 10%, #0aad0a 100%) 1; - padding-left: 3px; -} -.percentagebar100 { - border-left: 2px solid #0aad0a; - padding-left: 3px; -} - -.hidden, .ng-hide { display: none; } -.right { text-align: right; } -.center { text-align: center; } -.rightmargin { padding-right: 8px; } -.leftmargin { padding-left: 5px; } -.green { background-color: #0aad0a; } -.lightgreen { background-color: #dcf4dc; } -.red { background-color: #c10909; } -.lightred { background-color: #f7dede; } -.orange { background-color: #FFA500; } -.lightorange { background-color: #FFEFD5; } -.gray { background-color: #dcdcdc; } -.lightgray { color: #888888; } -.lightgraybg { background-color: #dadada; } - -.toggleZoom { text-align:right; } - -.ct-chart { position: relative; } -.ct-chart .ct-line { stroke-width: 2px !important; } -.ct-chart .ct-point { stroke-width: 6px !important; transition: stroke-width .2s; } -.ct-chart .ct-point:hover { stroke-width: 10px !important; } -.ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { stroke: #c00 !important;} -.ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { stroke: #1c2298 !important;} - -.tinylinecoveragechart, .tinybranchcoveragechart { background-color: #fff; margin-left: -3px; float: left; border: solid 1px #c1c1c1; width: 30px; height: 18px; } -.historiccoverageoffset { margin-top: 7px; } - -.tinylinecoveragechart .ct-line, .tinybranchcoveragechart .ct-line { stroke-width: 1px !important; } -.tinybranchcoveragechart .ct-series.ct-series-a .ct-line { stroke: #1c2298 !important; } - -.linecoverage { background-color: #c00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } -.branchcoverage { background-color: #1c2298; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } - -.tooltip { position: absolute; display: none; padding: 5px; background: #F4C63D;color: #453D3F; pointer-events: none; z-index: 1; } -.tooltip:after { content: ""; position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin-left: -15px; border: 15px solid transparent; border-top-color: #F4C63D; } - -.column1324 { max-width: 1324px; } -.column674 { max-width: 674px; } -.column60 { width: 60px; } -.column70 { width: 70px; } -.column90 { width: 90px; } -.column98 { width: 98px; } -.column100 { width: 100px; } -.column105 { width: 105px; } -.column112 { width: 112px; } -.column135 { width: 135px; } -.column150 { width: 150px; } - -.covered0 { width: 0px; } -.covered1 { width: 1px; } -.covered2 { width: 2px; } -.covered3 { width: 3px; } -.covered4 { width: 4px; } -.covered5 { width: 5px; } -.covered6 { width: 6px; } -.covered7 { width: 7px; } -.covered8 { width: 8px; } -.covered9 { width: 9px; } -.covered10 { width: 10px; } -.covered11 { width: 11px; } -.covered12 { width: 12px; } -.covered13 { width: 13px; } -.covered14 { width: 14px; } -.covered15 { width: 15px; } -.covered16 { width: 16px; } -.covered17 { width: 17px; } -.covered18 { width: 18px; } -.covered19 { width: 19px; } -.covered20 { width: 20px; } -.covered21 { width: 21px; } -.covered22 { width: 22px; } -.covered23 { width: 23px; } -.covered24 { width: 24px; } -.covered25 { width: 25px; } -.covered26 { width: 26px; } -.covered27 { width: 27px; } -.covered28 { width: 28px; } -.covered29 { width: 29px; } -.covered30 { width: 30px; } -.covered31 { width: 31px; } -.covered32 { width: 32px; } -.covered33 { width: 33px; } -.covered34 { width: 34px; } -.covered35 { width: 35px; } -.covered36 { width: 36px; } -.covered37 { width: 37px; } -.covered38 { width: 38px; } -.covered39 { width: 39px; } -.covered40 { width: 40px; } -.covered41 { width: 41px; } -.covered42 { width: 42px; } -.covered43 { width: 43px; } -.covered44 { width: 44px; } -.covered45 { width: 45px; } -.covered46 { width: 46px; } -.covered47 { width: 47px; } -.covered48 { width: 48px; } -.covered49 { width: 49px; } -.covered50 { width: 50px; } -.covered51 { width: 51px; } -.covered52 { width: 52px; } -.covered53 { width: 53px; } -.covered54 { width: 54px; } -.covered55 { width: 55px; } -.covered56 { width: 56px; } -.covered57 { width: 57px; } -.covered58 { width: 58px; } -.covered59 { width: 59px; } -.covered60 { width: 60px; } -.covered61 { width: 61px; } -.covered62 { width: 62px; } -.covered63 { width: 63px; } -.covered64 { width: 64px; } -.covered65 { width: 65px; } -.covered66 { width: 66px; } -.covered67 { width: 67px; } -.covered68 { width: 68px; } -.covered69 { width: 69px; } -.covered70 { width: 70px; } -.covered71 { width: 71px; } -.covered72 { width: 72px; } -.covered73 { width: 73px; } -.covered74 { width: 74px; } -.covered75 { width: 75px; } -.covered76 { width: 76px; } -.covered77 { width: 77px; } -.covered78 { width: 78px; } -.covered79 { width: 79px; } -.covered80 { width: 80px; } -.covered81 { width: 81px; } -.covered82 { width: 82px; } -.covered83 { width: 83px; } -.covered84 { width: 84px; } -.covered85 { width: 85px; } -.covered86 { width: 86px; } -.covered87 { width: 87px; } -.covered88 { width: 88px; } -.covered89 { width: 89px; } -.covered90 { width: 90px; } -.covered91 { width: 91px; } -.covered92 { width: 92px; } -.covered93 { width: 93px; } -.covered94 { width: 94px; } -.covered95 { width: 95px; } -.covered96 { width: 96px; } -.covered97 { width: 97px; } -.covered98 { width: 98px; } -.covered99 { width: 99px; } -.covered100 { width: 100px; } - - @media print { - html, body { background-color: #fff; } - .container { max-width: 100%; width: 100%; padding: 0; } - .overview colgroup col:first-child { width: 300px; } -} - -.icon-up-dir_active { - background-image: url(icon_up-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDEyMTZxMCAyNi0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMC00NS0xOXQtMTktNDUgMTktNDVsNDQ4LTQ0OHExOS0xOSA0NS0xOXQ0NSAxOWw0NDggNDQ4cTE5IDE5IDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-down-dir_active { - background-image: url(icon_up-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-down-dir { - background-image: url(icon_down-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-info-circled { - background-image: url(icon_info-circled.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; -} -.icon-plus { - background-image: url(icon_plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTQxNnY0MTZxMCA0MC0yOCA2OHQtNjggMjhoLTE5MnEtNDAgMC02OC0yOHQtMjgtNjh2LTQxNmgtNDE2cS00MCAwLTY4LTI4dC0yOC02OHYtMTkycTAtNDAgMjgtNjh0NjgtMjhoNDE2di00MTZxMC00MCAyOC02OHQ2OC0yOGgxOTJxNDAgMCA2OCAyOHQyOCA2OHY0MTZoNDE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-minus { - background-image: url(icon_minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTEyMTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGgxMjE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); - background-repeat: no-repeat; - background-size: contain; - padding-left: 15px; - height: 0.9em; - display: inline-block; - position: relative; - top: 3px; -} -.icon-wrench { - background-image: url(icon_wrench.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTQ0OCAxNDcycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em02NDQtNDIwbC02ODIgNjgycS0zNyAzNy05MCAzNy01MiAwLTkxLTM3bC0xMDYtMTA4cS0zOC0zNi0zOC05MCAwLTUzIDM4LTkxbDY4MS02ODFxMzkgOTggMTE0LjUgMTczLjV0MTczLjUgMTE0LjV6bTYzNC00MzVxMCAzOS0yMyAxMDYtNDcgMTM0LTE2NC41IDIxNy41dC0yNTguNSA4My41cS0xODUgMC0zMTYuNS0xMzEuNXQtMTMxLjUtMzE2LjUgMTMxLjUtMzE2LjUgMzE2LjUtMTMxLjVxNTggMCAxMjEuNSAxNi41dDEwNy41IDQ2LjVxMTYgMTEgMTYgMjh0LTE2IDI4bC0yOTMgMTY5djIyNGwxOTMgMTA3cTUtMyA3OS00OC41dDEzNS41LTgxIDcwLjUtMzUuNXExNSAwIDIzLjUgMTB0OC41IDI1eiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-fork { - background-image: url(icon_fork.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNmZmYiIC8+PHBhdGggZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-cube { - background-image: url(icon_cube.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTg5NiAxNjI5bDY0MC0zNDl2LTYzNmwtNjQwIDIzM3Y3NTJ6bS02NC04NjVsNjk4LTI1NC02OTgtMjU0LTY5OCAyNTR6bTgzMi0yNTJ2NzY4cTAgMzUtMTggNjV0LTQ5IDQ3bC03MDQgMzg0cS0yOCAxNi02MSAxNnQtNjEtMTZsLTcwNC0zODRxLTMxLTE3LTQ5LTQ3dC0xOC02NXYtNzY4cTAtNDAgMjMtNzN0NjEtNDdsNzA0LTI1NnEyMi04IDQ0LTh0NDQgOGw3MDQgMjU2cTM4IDE0IDYxIDQ3dDIzIDczeiIvPjwvc3ZnPg==); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-search-plus { - background-image: url(icon_search-plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtMjI0djIyNHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di0yMjRoLTIyNHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTY0cTAtMTMgOS41LTIyLjV0MjIuNS05LjVoMjI0di0yMjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41djIyNGgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} -.icon-search-minus { - background-image: url(icon_search-minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNTc2cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg1NzZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); - background-repeat: no-repeat; - background-size: contain; - padding-left: 20px; - height: 0.9em; - display: inline-block; -} - -.ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} \ No newline at end of file From 801bea6e0407825602c8db4dd8fed135883d2b1e Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 14:24:22 +0800 Subject: [PATCH 042/267] add miss file --- .../ConsoleAppNetFx48/Worker.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs new file mode 100644 index 00000000..e8cf870b --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace ConsoleAppNetFx48 +{ + public class Worker : BackgroundService + { + private readonly ILogger _logger; + + public Worker(ILogger logger) + { + this._logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + this._logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await Task.Delay(1000, stoppingToken); + } + } + } +} \ No newline at end of file From 4fd70b0467719569ae12d3ca179ff4321a4e22fd Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 18:18:54 +0800 Subject: [PATCH 043/267] add batch file --- .../CallSafeCreateService.bat | 15 +++ .../ConsoleAppNetFx48.csproj | 18 +++ .../ConsoleAppNetFx48/SafeCreateService.bat | 117 ++++++++++++++++++ .../ConsoleAppNetFx48/SafeDeleteService.bat | 78 ++++++++++++ .../ConsoleAppNetFx48/SafeStartService.bat | 66 ++++++++++ .../ConsoleAppNetFx48/SafeStopService.bat | 65 ++++++++++ .../ConsoleAppNetFx48/Worker.cs | 1 + 7 files changed, 360 insertions(+) create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat new file mode 100644 index 00000000..2db2e9fe --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat @@ -0,0 +1,15 @@ +@echo off +set batchFolder=%~dp0 +set serviceName=ConsoleAppNetFx48 +set serviceDisplayName=ConsoleAppNetFx48 +set serviceDescription="測試" +set serviceLaunchPath=%batchFolder%ConsoleAppNetFx48.exe +set serviceLogonId=.\setup +set serviceLogonPassword=pass@w0rd1~ +::set serverName=\\YAO-S658RF +set serverName= +Call SafeStopService %serviceName% %serverName% +Call SafeDeleteService %serviceName% %serverName% +Call SafeCreateService %serviceName% %serviceDisplayName% %serviceDescription% %serviceLaunchPath% %serviceLogonId% %serviceLogonPassword% %serverName% +::Call SafeStartService %serviceName% %serverName% + diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj index 1a8ca194..bf0e87b7 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -14,4 +14,22 @@ + + + + Always + + + Always + + + Always + + + Always + + + Always + + diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat new file mode 100644 index 00000000..b907a7f6 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat @@ -0,0 +1,117 @@ +@echo off + +IF [%1]==[] GOTO usage +IF [%2]==[] GOTO usage +IF [%3]==[] GOTO usage +IF [%4]==[] GOTO usage +IF [%5]==[] GOTO usage + +IF NOT "%1"=="" SET serviceName=%1 +IF NOT "%2"=="" SET serviceDisplayName=%2 +IF NOT "%3"=="" SET serviceDescription=%3 +IF NOT "%4"=="" SET serviceLaunchPath=%4 +IF NOT "%5"=="" SET serviceLogonId=%5 +IF NOT "%6"=="" SET serviceLogonPassword=%6 +IF NOT "%7"=="" SET serverName=%7 + +SC %serverName% query %serviceName% + +IF errorlevel 1060 GOTO ServiceNotFound +IF errorlevel 1722 GOTO SystemOffline +IF errorlevel 1001 GOTO DeletingServiceDelay + +:ResolveInitialState +SC %serverName% query %serviceName% | FIND "STATE" | FIND "RUNNING" + +IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" + +IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "PAUSED" + +IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline + +echo Service State is changing, waiting for service to resolve its state before making changes + +sc %serverName% query %serviceName% | Find "STATE" +ping -n 2 127.0.0.1 > NUL +GOTO ResolveInitialState + +:StopService +echo Stopping %serviceName% on %serverName% +sc %serverName% stop %serviceName% +GOTO StoppingService + +:StoppingServiceDelay +echo Waiting for %serviceName% to stop +ping -n 2 127.0.0.1 > NUL + +:StoppingService +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 1 GOTO StoppingServiceDelay + +:StoppedService +echo %serviceName% on %serverName% is stopped +GOTO DeleteService + +:DeleteService +echo Deleting %serviceName% on %serverName% +SC %serverName% delete %serviceName% + +:DeletingServiceDelay +echo Waiting for %serviceName% to get deleted +ping -n 2 127.0.0.1 > NUL + +:DeletingService +SC %serverName% query %serviceName% +IF NOT errorlevel 1060 GOTO DeletingServiceDelay + +:DeletedService +echo %serviceName% on %serverName% is deleted +GOTO CreateService + +:SystemOffline +echo Server %serverName% is not accessible or is offline +GOTO End + +:ServiceNotFound +echo Service %serviceName% is not installed on Server %serverName% +GOTO CreateService + +:CreateService +echo Creating %serviceName% on %serverName% +::SC %serverName% create %serviceName% binpath= "%serviceLaunchPath%" displayname= "THS MSMQ %serviceDisplayName% Agent" +SC %serverName% create %serviceName% binpath= "%serviceLaunchPath%" +SC %serverName% config %serviceName% displayname= "%serviceDisplayName%" +SC %serverName% config %serviceName% obj= %serviceLogonId% password= "%serviceLogonPassword%" +SC %serverName% config %serviceName% start= auto +SC %serverName% description %serviceName% "%serviceDescription%" +::SC "%serverName%" config "%serviceName%" type= share start= auto + +:CreatingServiceDelay +echo Waiting for %serviceName% to get created +ping -n 2 127.0.0.1 > NUL + +:CreatingService +::SC %serverName% query %serviceName% >NUL +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 1 GOTO CreatingServiceDelay + +:CreatedService +echo %serviceName% on %serverName% is created +GOTO End + +:usage +echo Will cause a local/remote service to START (if not already started). +echo This script will waiting for the service to enter the started state if necessary. +echo. +echo %0 [service name] [system name] +echo Example: %0 MyService server1 +echo Example: %0 MyService (for local PC) +echo. + +::GOTO:eof +:End +::pause \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat new file mode 100644 index 00000000..4c3057a5 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat @@ -0,0 +1,78 @@ +@echo off + +IF [%1]==[] GOTO usage +IF NOT "%1"=="" SET serviceName=%1 +IF NOT "%2"=="" SET serverName=%2 + +SC %serverName% query %serviceName% +IF errorlevel 1060 GOTO ServiceNotFound +IF errorlevel 1722 GOTO SystemOffline +IF errorlevel 1001 GOTO DeletingServiceDelay + +:ResolveInitialState +SC %serverName% query %serviceName% | FIND "STATE" | FIND "RUNNING" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "PAUSED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline +echo Service State is changing, waiting for service to resolve its state before making changes + +SC %serverName% query %serviceName% | Find "STATE" +ping -n 2 127.0.0.1 > NUL +GOTO ResolveInitialState + +:StopService +echo Stopping %serviceName% on %serverName% +SC %serverName% stop %serviceName% + +GOTO StoppingService +:StoppingServiceDelay +echo Waiting for %serviceName% to stop +ping -n 2 127.0.0.1 > NUL + +:StoppingService +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 1 GOTO StoppingServiceDelay + +:StoppedService +echo %serviceName% on %serverName% is stopped +GOTO DeleteService + +:DeleteService +SC %serverName% delete %serviceName% + +:DeletingServiceDelay +echo Waiting for %serviceName% to get deleted +ping -n 2 127.0.0.1 > NUL + +:DeletingService +SC %serverName% query %serviceName% +IF NOT errorlevel 1060 GOTO DeletingServiceDelay + +:DeletedService +echo %serviceName% on %serverName% is deleted +GOTO End + +:SystemOffline +echo Server %serverName% is not accessible or is offline +GOTO End + +:ServiceNotFound +echo Service %serviceName% is not installed on Server %serverName% +::exit /b 0 +GOTO End + +:usage +echo Will cause a local/remote service to START (if not already started). +echo This script will waiting for the service to enter the started state if necessary. +echo. +echo %0 [service name] [system name] +echo Example: %0 MyService server1 +echo Example: %0 MyService (for local PC) +echo. + +:End +::pause \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat new file mode 100644 index 00000000..90f24eae --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat @@ -0,0 +1,66 @@ +@echo off + +IF [%1]==[] GOTO usage +IF NOT "%1"=="" SET serviceName=%1 +IF NOT "%2"=="" SET serverName=%2 + +SC %serverName% query %serviceName% +IF errorlevel 1060 GOTO ServiceNotFound +IF errorlevel 1722 GOTO SystemOffline + +:ResolveInitialState + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "RUNNING" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StartedService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "PAUSED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline +echo Service State is changing, waiting for service to resolve its state before making changes + +SC %serverName% query %serviceName% | Find "STATE" >NUL +ping -n 2 127.0.0.1 > NUL + +GOTO ResolveInitialState + +:StartService +echo Starting %serviceName% on %serverName% +SC %serverName% start %serviceName% + +GOTO StartingService + +:StartingServiceDelay +echo Waiting for %serviceName% to start +ping -n 2 127.0.0.1 > NUL + +:StartingService +SC %serverName% query %serviceName% | FIND "STATE" | FIND "RUNNING" +IF errorlevel 1 GOTO StartingServiceDelay + +:StartedService +echo %serviceName% on %serverName% is started +GOTO End + +:SystemOffline +echo Server %serverName% is not accessible or is offline +GOTO End + +:ServiceNotFound +echo Service %serviceName% is not installed on Server %serverName% +::exit /b 0 +GOTO End + +:usage +echo Will cause a local/remote service to START (if not already started). +echo This script will waiting for the service to enter the started state if necessary. +echo. +echo %0 [service name] [system name] +echo Example: %0 MyService server1 +echo Example: %0 MyService (for local PC) +echo. + +::GOTO:eof +:End +::pause \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat new file mode 100644 index 00000000..7bdadfc4 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat @@ -0,0 +1,65 @@ +@echo off + +IF [%1]==[] GOTO usage +IF NOT "%1"=="" SET serviceName=%1 +IF NOT "%2"=="" SET serverName=%2 + +SC %serverName% query %serviceName% +IF errorlevel 1060 GOTO ServiceNotFound +IF errorlevel 1722 GOTO SystemOffline + +:ResolveInitialState +SC %serverName% query %serviceName% | FIND "STATE" | FIND "RUNNING" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService + +SC %serverName% query %serviceName% | FIND "STATE" | FIND "PAUSED" +IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline +echo Service State is changing, waiting for service to resolve its state before making changes + +SC %serverName% query %serviceName% | Find "STATE" +ping -n 2 127.0.0.1 > NUL +GOTO ResolveInitialState + +:StopService +echo Stopping %serviceName% on %serverName% +SC %serverName% stop %serviceName% +GOTO StoppingService + +:StoppingServiceDelay +echo Waiting for %serviceName% to stop +ping -n 2 127.0.0.1 > NUL + +:StoppingService +SC %serverName% query %serviceName% | FIND "STATE" | FIND "STOPPED" +IF errorlevel 1 GOTO StoppingServiceDelay + +:StoppedService +echo %serviceName% on %serverName% is stopped +GOTO End + +:SystemOffline +echo Server %serverName% is not accessible or is offline +GOTO End + +:ServiceNotFound +echo Service %serviceName% is not installed on Server %serverName% +::exit /b 0 +GOTO End + +:usage +echo Will cause a local/remote service to STOP (if not already stopped). +echo This script will waiting for the service to enter the stopped state if necessary. +echo. +echo %0 [service name] [system name] {reason} +echo Example: %0 MyService server1 {reason} +echo Example: %0 MyService (for local PC, DO NOT specify reason) +echo. +echo For reason codes, run "sc stop" + + +::GOTO:eof +:End +::pause \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs index e8cf870b..e2c4435f 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; From ea8c40bd40e61feef066b833e4722504f8b36161 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 18:37:20 +0800 Subject: [PATCH 044/267] add batch file --- .../ConsoleAppNetFx48/CallSafeCreateService.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat index 2db2e9fe..5730ad38 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat @@ -2,12 +2,12 @@ set batchFolder=%~dp0 set serviceName=ConsoleAppNetFx48 set serviceDisplayName=ConsoleAppNetFx48 -set serviceDescription="測試" +set serviceDescription="" set serviceLaunchPath=%batchFolder%ConsoleAppNetFx48.exe set serviceLogonId=.\setup -set serviceLogonPassword=pass@w0rd1~ +set serviceLogonPassword=password ::set serverName=\\YAO-S658RF -set serverName= +set serverName="" Call SafeStopService %serviceName% %serverName% Call SafeDeleteService %serviceName% %serverName% Call SafeCreateService %serviceName% %serviceDisplayName% %serviceDescription% %serviceLaunchPath% %serviceLogonId% %serviceLogonPassword% %serverName% From aba89a366f9b24a4eb44c7b01c47c2c687edd290 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 18:40:32 +0800 Subject: [PATCH 045/267] add batch file --- .../ConsoleAppNetFx48/CallSafeCreateService.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat index 5730ad38..e4a37b2c 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat @@ -7,7 +7,7 @@ set serviceLaunchPath=%batchFolder%ConsoleAppNetFx48.exe set serviceLogonId=.\setup set serviceLogonPassword=password ::set serverName=\\YAO-S658RF -set serverName="" +set serverName= Call SafeStopService %serviceName% %serverName% Call SafeDeleteService %serviceName% %serverName% Call SafeCreateService %serviceName% %serviceDisplayName% %serviceDescription% %serviceLaunchPath% %serviceLogonId% %serviceLogonPassword% %serverName% From 18be3d01d1479686c3b4696775190fef992aabc7 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 18:48:52 +0800 Subject: [PATCH 046/267] add batch file --- .../ConsoleAppNetFx48/CallSafeCreateService.bat | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat index e4a37b2c..5d5b9686 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat @@ -3,13 +3,13 @@ set batchFolder=%~dp0 set serviceName=ConsoleAppNetFx48 set serviceDisplayName=ConsoleAppNetFx48 set serviceDescription="" -set serviceLaunchPath=%batchFolder%ConsoleAppNetFx48.exe +set serviceLaunchPath=%batchFolder%bin\ConsoleAppNetFx48.exe set serviceLogonId=.\setup -set serviceLogonPassword=password -::set serverName=\\YAO-S658RF +set serviceLogonPassword=pass@w0rd1~ +::set serverName=\\Computer Name set serverName= Call SafeStopService %serviceName% %serverName% Call SafeDeleteService %serviceName% %serverName% Call SafeCreateService %serviceName% %serviceDisplayName% %serviceDescription% %serviceLaunchPath% %serviceLogonId% %serviceLogonPassword% %serverName% -::Call SafeStartService %serviceName% %serverName% +Call SafeStartService %serviceName% %serverName% From d50a334ff0ef5e7b31be2c9435e7b858e88e9e3d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 7 Apr 2021 18:49:16 +0800 Subject: [PATCH 047/267] add batch file --- .../ConsoleAppNetFx48/CallSafeCreateService.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat index 5d5b9686..8e500b2b 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/CallSafeCreateService.bat @@ -5,7 +5,7 @@ set serviceDisplayName=ConsoleAppNetFx48 set serviceDescription="" set serviceLaunchPath=%batchFolder%bin\ConsoleAppNetFx48.exe set serviceLogonId=.\setup -set serviceLogonPassword=pass@w0rd1~ +set serviceLogonPassword=password ::set serverName=\\Computer Name set serverName= Call SafeStopService %serviceName% %serverName% From 7221b37673a340abc0b7fb114b1a164753147e38 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 8 Apr 2021 11:38:23 +0800 Subject: [PATCH 048/267] refactor --- Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat | 3 +-- Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat | 1 - Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat | 3 +-- Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat | 3 +-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat index b907a7f6..5851866c 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeCreateService.bat @@ -113,5 +113,4 @@ echo Example: %0 MyService (for local PC) echo. ::GOTO:eof -:End -::pause \ No newline at end of file +:End \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat index 4c3057a5..1e045a82 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeDeleteService.bat @@ -75,4 +75,3 @@ echo Example: %0 MyService (for local PC) echo. :End -::pause \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat index 90f24eae..98bdf838 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStartService.bat @@ -62,5 +62,4 @@ echo Example: %0 MyService (for local PC) echo. ::GOTO:eof -:End -::pause \ No newline at end of file +:End \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat index 7bdadfc4..952915f2 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/SafeStopService.bat @@ -61,5 +61,4 @@ echo For reason codes, run "sc stop" ::GOTO:eof -:End -::pause \ No newline at end of file +:End \ No newline at end of file From 7195b7effba1d248cfc929247ba0cdec7aec8605 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 8 Apr 2021 14:58:44 +0800 Subject: [PATCH 049/267] refactor --- .../ConsoleAppNetFx48.csproj | 4 +- .../ConsoleAppNetFx48/Program.cs | 51 +++++++++---------- .../ConsoleAppNetFx48/Worker.cs | 6 ++- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj index bf0e87b7..6a9de990 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs index cc743c08..89547e27 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs @@ -1,38 +1,37 @@ +using System; +using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Topshelf; +using Topshelf.Extensions.Hosting; +using Host = Microsoft.Extensions.Hosting.Host; namespace ConsoleAppNetFx48 { public class Program { - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseWindowsService() - .ConfigureServices((hostContext, services) => - { - services.AddHostedService(); - }); - - public static void Main(string[] args) + private static void Main(string[] args) { - // HostFactory.Run(x => - // { - // x.Service(s => - // { - // s.ConstructUsing(name => new DoThing()); - // s.WhenStarted(tc => tc.Start()); - // s.WhenStopped(tc => tc.Stop()); - // }); - // x.UseNLog(); - // x.RunAsLocalSystem(); - // var assemblyName = Assembly.GetEntryAssembly().GetName().Name; - // x.SetDescription("Sample Topshelf Host"); - // x.SetDisplayName(assemblyName); - // x.SetServiceName(assemblyName); - // }); - var hostBuilder = CreateHostBuilder(args); - hostBuilder.Build().Run(); + + var exitCode = + hostBuilder.RunAsTopshelfService(config => + { + var assemblyName = Assembly.GetEntryAssembly().GetName().Name; + config.SetServiceName(assemblyName); + config.SetDisplayName(assemblyName); + config.SetDescription("Runs a generic host as a Topshelf service."); + config.RunAsPrompt(); + }); + Console.WriteLine($"服務控制狀態:{exitCode}"); + // hostBuilder.Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + // .UseWindowsService() + .ConfigureServices((hostContext, services) => { services.AddHostedService(); }); } } } \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs index e2c4435f..5609eb04 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Worker.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -11,7 +10,10 @@ public class Worker : BackgroundService { private readonly ILogger _logger; - public Worker(ILogger logger) + public Worker(ILogger logger, + IHostApplicationLifetime appLifetime, + IHostLifetime hostLifetime, + IHostEnvironment hostEnvironment) { this._logger = logger; } From e2c2f43c1f611e4cbbc03522e7e81bf9b58389a4 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 8 Apr 2021 16:02:30 +0800 Subject: [PATCH 050/267] add Topshelf.Extensions.Configuration --- .../ConsoleAppNetFx48.csproj | 16 ++++++++++- .../ConsoleAppNetFx48/Player.cs | 9 ++++++ .../ConsoleAppNetFx48/Program.cs | 28 +++++++++++++++---- .../ConsoleAppNetFx48/appsettings.json | 11 ++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 Host/Lab.WorkerService/ConsoleAppNetFx48/Player.cs diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj index 6a9de990..eacddb05 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/ConsoleAppNetFx48.csproj @@ -11,7 +11,8 @@ - + + @@ -32,4 +33,17 @@ Always + + + + true + Always + PreserveNewest + + + true + Always + PreserveNewest + + diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Player.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Player.cs new file mode 100644 index 00000000..8969ad99 --- /dev/null +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Player.cs @@ -0,0 +1,9 @@ +namespace ConsoleAppNetFx48 +{ + public struct Player + { + public string AppId { get; set; } + + public string Key { get; set; } + } +} \ No newline at end of file diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs index 89547e27..814cb9a2 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/Program.cs @@ -1,8 +1,12 @@ using System; +using System.IO; using System.Reflection; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Topshelf; +using Topshelf.Configuration; using Topshelf.Extensions.Hosting; using Host = Microsoft.Extensions.Hosting.Host; @@ -17,19 +21,33 @@ private static void Main(string[] args) var exitCode = hostBuilder.RunAsTopshelfService(config => { - var assemblyName = Assembly.GetEntryAssembly().GetName().Name; - config.SetServiceName(assemblyName); - config.SetDisplayName(assemblyName); - config.SetDescription("Runs a generic host as a Topshelf service."); - config.RunAsPrompt(); + // var assemblyName = Assembly.GetEntryAssembly().GetName().Name; + // config.SetServiceName(assemblyName); + // config.SetDisplayName(assemblyName); + // config.SetDescription("Runs a generic host as a Topshelf service."); + // config.RunAsPrompt(); + var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddConsole(); + }); + config.UseLoggingExtensions(loggerFactory); + + var configRoot = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + var topshelfSection = configRoot.GetSection("Topshelf"); + config.ApplyConfiguration(topshelfSection); }); Console.WriteLine($"服務控制狀態:{exitCode}"); + // hostBuilder.Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) + // .UseWindowsService() .ConfigureServices((hostContext, services) => { services.AddHostedService(); }); } diff --git a/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json index 8983e0fc..b8026813 100644 --- a/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json +++ b/Host/Lab.WorkerService/ConsoleAppNetFx48/appsettings.json @@ -5,5 +5,16 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "Topshelf": { + "ServiceName": "ConsoleAppNetFx48", + "DisplayName": "ConsoleAppNetFx48", + "Description":"Runs a generic host as a Topshelf service.", + "Instance":"1", + "Account":{ + "Username":".\\setup", + "Password":"password" + }, + "StopTimeout":"60" } } From ad476db1a2400a00d8d6d72b2012afebd0e0ec4a Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 9 Apr 2021 11:45:19 +0800 Subject: [PATCH 051/267] add user secret test --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 5 ++- .../NetFx48/SurveyUserSecretTests.cs | 42 +++++++++++++++++++ .../Lab.Config/NetFx48/appsettings.json | 3 +- 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index 954e0d74..c347c8c3 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -1,4 +1,4 @@ - + net48 @@ -10,7 +10,8 @@ Debug;Release;QA AnyCPU - + 659be13b-676e-4c9e-a0b9-0df2ffd75cfc + true diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs new file mode 100644 index 00000000..67e005f8 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyUserSecretTests + { + [TestMethod] + public void HostŪK() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureHostConfiguration(config => + { + config.AddJsonFile("appsettings.json", false, true); + }) + ; + var host = builder.Build(); + + var config = host.Services.GetService(); + Console.WriteLine($"Player:Key = {config["Player:Key"]}"); + Console.WriteLine($"DbPassword = {config["DbPassword"]}"); + } + + [TestMethod] + public void ʹҤƲպAŪK() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddUserSecrets() + ; + + var config = builder.Build(); + Console.WriteLine($"Player:Key = {config["Player:Key"]}"); + } + } +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json index db5d9362..52fc1bcc 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json @@ -6,5 +6,6 @@ "AppId": "player1", "Key": "1234567890" }, - "Environment": "Development" + "Environment": "Development", + "ApplicationName":"NetFx48" } \ No newline at end of file From 985ba5f19471ef04f51fa42a7c499973063f0d6b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 9 Apr 2021 23:19:23 +0800 Subject: [PATCH 052/267] =?UTF-8?q?=E8=AA=BF=E6=95=B4=E6=9E=B6=E6=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.DAL.TestProject.csproj | 6 ++ .../Lab.DAL.TestProject/UnitTest1.cs | 24 ++++- .../Lab.DAL/DbContextOptionManager.cs | 31 ------ .../Lab.DAL/DefaultDbContextManager.cs | 94 +++++++++++++++++++ .../DomainModel/Employee/InsertRequest.cs | 6 ++ .../Lab.DAL/EmployeeContextFactory.cs | 19 ---- .../Lab.DAL/EmployeeRepository.cs | 38 ++++++++ .../Lab.DAL/EntityModel/EmployeeContext.cs | 48 +++++++--- .../Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 2 +- 9 files changed, 203 insertions(+), 65 deletions(-) delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbContextOptionManager.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index 7b4935b5..e94f8450 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -12,6 +12,12 @@ + + + + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index 21530ddd..314999cd 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -1,7 +1,10 @@ using System; using System.Linq; +using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.DAL.UnitTest @@ -12,7 +15,7 @@ public class UnitTest1 [TestMethod] public void TestMethod1() { - var options = DbContextOptionManager.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextManager.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { var employees = dbContext.Employees.AsNoTracking().ToList(); @@ -22,7 +25,7 @@ public void TestMethod1() [TestMethod] public void TestMethod2() { - var options = DbContextOptionManager.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextManager.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { @@ -39,5 +42,22 @@ public void TestMethod2() Assert.AreEqual(true, toDb.SequenceId != 0); } } + + [TestMethod] + public void 注入DbContextFactor操作真實資料庫() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(service => + { + service + .AddDbContextFactory(DefaultDbContextManager + .ApplyConfigurePhysical); + service.AddSingleton(); + }); + var host = builder.Build(); + var repository = host.Services.GetService(); + var count = repository.InsertAsync(new InsertRequest(), "").Result; + Assert.AreEqual(1, count); + } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbContextOptionManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbContextOptionManager.cs deleted file mode 100644 index 851b770f..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbContextOptionManager.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Lab.DAL.EntityModel; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Lab.DAL -{ - public class DbContextOptionManager - { - public static DbContextOptions CreateEmployeeDbContextOptions() - { - var configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - var connectionString = configuration.GetConnectionString("DefaultConnection"); - var loggerFactory = LoggerFactory.Create(builder => - { - builder - //.AddFilter("Microsoft", LogLevel.Warning) - //.AddFilter("System", LogLevel.Warning) - .AddFilter("Lab.DAL", LogLevel.Debug) - .AddConsole() - ; - }); - return new DbContextOptionsBuilder() - .UseSqlServer(connectionString) - .UseLoggerFactory(loggerFactory) - .Options; - } - } -} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs new file mode 100644 index 00000000..156a99f7 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -0,0 +1,94 @@ +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.DAL +{ + public class DefaultDbContextManager + { + public static void ApplyConfigureMemory(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + var configuration = provider.GetService(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + optionsBuilder.UseSqlServer(connectionString) + .UseInMemoryDatabase("Demo") + .UseLoggerFactory(loggerFactory) + ; + } + + public static void ApplyConfigurePhysical(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + var configuration = provider.GetService(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + } + public static DbContextOptionsBuilder CreateEmployeeDbContextOptionsBuilder() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + return new DbContextOptionsBuilder() + .UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + } + + public static DbContextOptions CreateEmployeeDbContextOptions() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + return new DbContextOptionsBuilder() + .UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + .Options; + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs new file mode 100644 index 00000000..243a534c --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs @@ -0,0 +1,6 @@ +namespace Lab.DAL.DomainModel.Employee +{ + public class InsertRequest + { + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs deleted file mode 100644 index 3214e39e..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Lab.DAL.EntityModel; -using Microsoft.EntityFrameworkCore; - -namespace Lab.DAL -{ - public class EmployeeContextFactory - { - private DbContextOptions _options; - - public EmployeeContextFactory(DbContextOptions options) - { - this._options = options; - } - - public EmployeeContextFactory():this(DbContextOptionManager.CreateEmployeeDbContextOptions()) - { - } - } -} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs new file mode 100644 index 00000000..a2eddcbb --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.DAL +{ + public class EmployeeRepository + { + private readonly IDbContextFactory _factory; + + public EmployeeRepository(IDbContextFactory factory) + { + this._factory = factory; + } + + // public EmployeeRepository(EmployeeContext factory) + // { + // } + public async Task InsertAsync(InsertRequest request, + string accessId, + CancellationToken cancel = default) + { + using var dbContext = this._factory.CreateDbContext(); + var id = Guid.NewGuid(); + var toDb = new Employee + { + Id = id, + Name = "yao", + Age = 18, + }; + await dbContext.Employees.AddAsync(toDb, cancel); + return await dbContext.SaveChangesAsync(cancel); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 142d3f56..6be4b774 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; namespace Lab.DAL.EntityModel { @@ -12,14 +13,36 @@ public class EmployeeContext : DbContext public virtual DbSet Orders { get; set; } - public EmployeeContext() - { - - } + // public EmployeeContext() + // { + // + // } + // public EmployeeContext(string connectionString) + // { + // this._connectionString = connectionString; + // if (!s_migrated[0]) + // { + // lock (s_migrated) + // { + // if (!s_migrated[0]) + // { + // this.Database.Migrate(); + // s_migrated[0] = true; + // } + // } + // } + // } + public string ConnectionString { get; } public EmployeeContext(DbContextOptions options) : base(options) { + var sqlServerOptionsExtension = options.FindExtension(); + if (sqlServerOptionsExtension != null) + { + this.ConnectionString = sqlServerOptionsExtension.ConnectionString; + } + if (!s_migrated[0]) { lock (s_migrated) @@ -33,16 +56,17 @@ public EmployeeContext(DbContextOptions options) } } + // 建構函數配置失敗才需要以下處理 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { -// //var connectionString = "Server=(localdb)\\mssqllocaldb;Database=LabEmployee.DAL;Trusted_Connection=True;"; -// var connectionString = DbOptionsFactory.ConnectionString; -// if (!optionsBuilder.IsConfigured) -// { -// #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings. -// optionsBuilder -// .UseSqlServer(connectionString); -// } + // var connectionString = + // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; + // + // // var connectionString = this._connectionString; + // if (optionsBuilder.IsConfigured == false) + // { + // optionsBuilder.UseSqlServer(connectionString); + // } } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index 99c5ead8..4fb1d415 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -7,10 +7,10 @@ + - From d3651c7e7afbd03bd49896c25ae4189e2e50869d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sat, 10 Apr 2021 16:42:15 +0800 Subject: [PATCH 053/267] =?UTF-8?q?=E8=AA=BF=E6=95=B4=E6=9E=B6=E6=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.Biz/IEmployeeRepository.cs | 8 +++ .../Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj | 7 +++ .../Lab.DAL.TestProject/UnitTest1.cs | 30 +++++++--- .../Lab.VirtualDb/Lab.DAL/DbOptionsFactory.cs | 37 ------------ ...tManager.cs => DefaultDbContextBuilder.cs} | 58 +++++++++---------- .../Lab.DAL/EmployeeRepository.cs | 13 ++--- .../Lab.DAL/EntityModel/EmployeeContext.cs | 38 +++--------- ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln | 6 ++ 8 files changed, 86 insertions(+), 111 deletions(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbOptionsFactory.cs rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/{DefaultDbContextManager.cs => DefaultDbContextBuilder.cs} (65%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs new file mode 100644 index 00000000..5f0cc7f0 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs @@ -0,0 +1,8 @@ +using System; + +namespace Lab.Biz +{ + public interface IEmployeeRepository + { + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj new file mode 100644 index 00000000..cbfa5815 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index 314999cd..eee4ed3a 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -15,7 +15,7 @@ public class UnitTest1 [TestMethod] public void TestMethod1() { - var options = DefaultDbContextManager.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextBuilder.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { var employees = dbContext.Employees.AsNoTracking().ToList(); @@ -25,7 +25,7 @@ public void TestMethod1() [TestMethod] public void TestMethod2() { - var options = DefaultDbContextManager.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextBuilder.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { @@ -47,12 +47,28 @@ public void TestMethod2() public void 注入DbContextFactor操作真實資料庫() { var builder = Host.CreateDefaultBuilder() - .ConfigureServices(service => + .ConfigureServices(services => { - service - .AddDbContextFactory(DefaultDbContextManager - .ApplyConfigurePhysical); - service.AddSingleton(); + services + .AddDbContextFactory + (DefaultDbContextBuilder.ApplyConfigurePhysical); + services.AddSingleton(); + }); + var host = builder.Build(); + var repository = host.Services.GetService(); + var count = repository.InsertAsync(new InsertRequest(), "").Result; + Assert.AreEqual(1, count); + } + + [TestMethod] + public void 注入DbContextFactor操作記憶體() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddDbContextFactory + (DefaultDbContextBuilder.ApplyConfigureMemory); + services.AddSingleton(); }); var host = builder.Build(); var repository = host.Services.GetService(); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbOptionsFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbOptionsFactory.cs deleted file mode 100644 index 059eec32..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DbOptionsFactory.cs +++ /dev/null @@ -1,37 +0,0 @@ -// using Lab.DAL.EntityModel; -// using Microsoft.EntityFrameworkCore; -// using Microsoft.Extensions.Configuration; -// using Microsoft.Extensions.Logging; -// -// namespace Lab.DAL -// { -// public class DbOptionsFactory -// { -// public static DbContextOptions DbContextOptions { get; } -// -// public static string ConnectionString { get; } -// public static readonly ILoggerFactory MyLoggerFactory -// = LoggerFactory.Create(builder => { builder.AddConsole(); }); -// -// static DbOptionsFactory() -// { -// var configuration = new ConfigurationBuilder() -// .AddJsonFile("appsettings.json") -// .Build(); -// ConnectionString = configuration.GetConnectionString("DefaultConnection"); -// var loggerFactory = LoggerFactory.Create(builder => -// { -// builder -// //.AddFilter("Microsoft", LogLevel.Warning) -// //.AddFilter("System", LogLevel.Warning) -// .AddFilter("Lab.DAL", LogLevel.Debug) -// .AddConsole() -// ; -// }); -// DbContextOptions = new DbContextOptionsBuilder() -// .UseSqlServer(ConnectionString) -// .UseLoggerFactory(loggerFactory) -// .Options; -// } -// } -// } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs similarity index 65% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs index 156a99f7..4e7c7d1e 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs @@ -7,13 +7,24 @@ namespace Lab.DAL { - public class DefaultDbContextManager + public class DefaultDbContextBuilder { - public static void ApplyConfigureMemory(IServiceProvider provider, - DbContextOptionsBuilder optionsBuilder) + private IServiceProvider _serviceProvider; + + public DefaultDbContextBuilder(IServiceCollection services) + { + if (services == null) + { + services = new ServiceCollection(); + } + services.AddDbContextFactory(ApplyConfigurePhysical); + this._serviceProvider = services.BuildServiceProvider(); + services.AddSingleton(); + } + + public static void ApplyConfigureMemory(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) { - var configuration = provider.GetService(); - var connectionString = configuration.GetConnectionString("DefaultConnection"); var loggerFactory = LoggerFactory.Create(builder => { builder @@ -24,14 +35,13 @@ public static void ApplyConfigureMemory(IServiceProvider provider, .AddConsole() ; }); - optionsBuilder.UseSqlServer(connectionString) - .UseInMemoryDatabase("Demo") + optionsBuilder.UseInMemoryDatabase("Demo") .UseLoggerFactory(loggerFactory) ; } - public static void ApplyConfigurePhysical(IServiceProvider provider, - DbContextOptionsBuilder optionsBuilder) + public static void ApplyConfigurePhysical(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) { var configuration = provider.GetService(); var connectionString = configuration.GetConnectionString("DefaultConnection"); @@ -49,28 +59,13 @@ public static void ApplyConfigurePhysical(IServiceProvider provider, .UseLoggerFactory(loggerFactory) ; } - public static DbContextOptionsBuilder CreateEmployeeDbContextOptionsBuilder() + + public static DbContextOptions CreateEmployeeDbContextOptions() { - var configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - var connectionString = configuration.GetConnectionString("DefaultConnection"); - var loggerFactory = LoggerFactory.Create(builder => - { - builder - //.AddFilter("Microsoft", LogLevel.Warning) - //.AddFilter("System", LogLevel.Warning) - .AddFilter("Lab.DAL", LogLevel.Debug) - .AddConsole() - ; - }); - return new DbContextOptionsBuilder() - .UseSqlServer(connectionString) - .UseLoggerFactory(loggerFactory) - ; - } + return CreateEmployeeDbContextOptionsBuilder().Options; + } - public static DbContextOptions CreateEmployeeDbContextOptions() + public static DbContextOptionsBuilder CreateEmployeeDbContextOptionsBuilder() { var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") @@ -79,6 +74,7 @@ public static DbContextOptions CreateEmployeeDbContextOptions( var loggerFactory = LoggerFactory.Create(builder => { builder + //.AddFilter("Microsoft", LogLevel.Warning) //.AddFilter("System", LogLevel.Warning) .AddFilter("Lab.DAL", LogLevel.Debug) @@ -88,7 +84,7 @@ public static DbContextOptions CreateEmployeeDbContextOptions( return new DbContextOptionsBuilder() .UseSqlServer(connectionString) .UseLoggerFactory(loggerFactory) - .Options; - } + ; + } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index a2eddcbb..d5a5db8b 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -11,14 +11,13 @@ public class EmployeeRepository { private readonly IDbContextFactory _factory; - public EmployeeRepository(IDbContextFactory factory) - { - this._factory = factory; - } - - // public EmployeeRepository(EmployeeContext factory) + // public EmployeeRepository(IDbContextFactory factory) // { + // this._factory = factory; // } + public EmployeeRepository(EmployeeContext factory) + { + } public async Task InsertAsync(InsertRequest request, string accessId, CancellationToken cancel = default) @@ -27,7 +26,7 @@ public async Task InsertAsync(InsertRequest request, var id = Guid.NewGuid(); var toDb = new Employee { - Id = id, + Id = id, Name = "yao", Age = 18, }; diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 6be4b774..4660cb3f 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; namespace Lab.DAL.EntityModel { @@ -13,43 +13,23 @@ public class EmployeeContext : DbContext public virtual DbSet Orders { get; set; } - // public EmployeeContext() - // { - // - // } - // public EmployeeContext(string connectionString) - // { - // this._connectionString = connectionString; - // if (!s_migrated[0]) - // { - // lock (s_migrated) - // { - // if (!s_migrated[0]) - // { - // this.Database.Migrate(); - // s_migrated[0] = true; - // } - // } - // } - // } public string ConnectionString { get; } public EmployeeContext(DbContextOptions options) : base(options) { - var sqlServerOptionsExtension = options.FindExtension(); - if (sqlServerOptionsExtension != null) - { - this.ConnectionString = sqlServerOptionsExtension.ConnectionString; - } - - if (!s_migrated[0]) + if (s_migrated[0] == false) { lock (s_migrated) { - if (!s_migrated[0]) + if (s_migrated[0] == false) { - this.Database.Migrate(); + var memoryOptions = options.FindExtension(); + if (memoryOptions == null) + { + this.Database.Migrate(); + } + s_migrated[0] = true; } } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln index 2d4c828a..f401e0a4 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln +++ b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL", "Lab.DAL\Lab.DAL. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL.TestProject", "Lab.DAL.TestProject\Lab.DAL.TestProject.csproj", "{3DAC8D3D-E494-459B-BEAD-AD306034E441}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Biz", "Lab.Biz\Lab.Biz.csproj", "{0056BEF7-8B47-4387-9110-788A3B73E452}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Debug|Any CPU.Build.0 = Debug|Any CPU {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Release|Any CPU.Build.0 = Release|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 7f3e111371c1524e6afe1dcf6ac5ce79b778e447 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sun, 11 Apr 2021 23:25:53 +0800 Subject: [PATCH 054/267] refactor --- .../Lab.DAL.TestProject/UnitTest1.cs | 19 ++--- ...tBuilder.cs => DefaultDbContextFactory.cs} | 76 ++++++++++++++++--- .../Lab.DAL/EmployeeRepository.cs | 32 +++++--- .../Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 6 +- 4 files changed, 102 insertions(+), 31 deletions(-) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/{DefaultDbContextBuilder.cs => DefaultDbContextFactory.cs} (60%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index eee4ed3a..fe4cceb1 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -3,6 +3,7 @@ using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -15,7 +16,7 @@ public class UnitTest1 [TestMethod] public void TestMethod1() { - var options = DefaultDbContextBuilder.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextFactory.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { var employees = dbContext.Employees.AsNoTracking().ToList(); @@ -25,7 +26,7 @@ public void TestMethod1() [TestMethod] public void TestMethod2() { - var options = DefaultDbContextBuilder.CreateEmployeeDbContextOptions(); + var options = DefaultDbContextFactory.CreateEmployeeDbContextOptions(); using (var dbContext = new EmployeeContext(options)) { @@ -43,15 +44,14 @@ public void TestMethod2() } } + + [TestMethod] - public void 注入DbContextFactor操作真實資料庫() + public void 注入DbContextFactory操作真實資料庫() { var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services - .AddDbContextFactory - (DefaultDbContextBuilder.ApplyConfigurePhysical); services.AddSingleton(); }); var host = builder.Build(); @@ -61,16 +61,17 @@ public void 注入DbContextFactor操作真實資料庫() } [TestMethod] - public void 注入DbContextFactor操作記憶體() + public void 注入DbContextFactory操作記憶體() { var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services.AddDbContextFactory - (DefaultDbContextBuilder.ApplyConfigureMemory); services.AddSingleton(); }); var host = builder.Build(); + + DefaultDbContextFactory.UseMemory(); + var repository = host.Services.GetService(); var count = repository.InsertAsync(new InsertRequest(), "").Result; Assert.AreEqual(1, count); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs similarity index 60% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs index 4e7c7d1e..e2b97023 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextBuilder.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -7,19 +8,59 @@ namespace Lab.DAL { - public class DefaultDbContextBuilder + public static class DefaultDbContextFactory { - private IServiceProvider _serviceProvider; + private static readonly Lazy s_serviceProviderLazy; + private static readonly Lazy s_configurationLazy; + private static ServiceProvider s_serviceProvider; + private static IConfiguration s_configuration; - public DefaultDbContextBuilder(IServiceCollection services) + public static ServiceProvider ServiceProvider { - if (services == null) + get { - services = new ServiceCollection(); + if (s_serviceProvider == null) + { + s_serviceProvider = s_serviceProviderLazy.Value; + } + + return s_serviceProvider; + } + set => s_serviceProvider = value; + } + + public static IConfiguration Configuration + { + get + { + if (s_configuration == null) + { + s_configuration = s_configurationLazy.Value; + } + + return s_configuration; } - services.AddDbContextFactory(ApplyConfigurePhysical); - this._serviceProvider = services.BuildServiceProvider(); - services.AddSingleton(); + set => s_configuration = value; + } + + static DefaultDbContextFactory() + { + s_serviceProviderLazy = + new Lazy(() => + { + var services = new ServiceCollection(); + services.AddDbContextFactory(ApplyConfigurePhysical); + + return services.BuildServiceProvider(); + }); + s_configurationLazy + = new Lazy(() => + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + return configBuilder.Build(); + }); } public static void ApplyConfigureMemory(IServiceProvider provider, @@ -43,7 +84,12 @@ public static void ApplyConfigureMemory(IServiceProvider provider, public static void ApplyConfigurePhysical(IServiceProvider provider, DbContextOptionsBuilder optionsBuilder) { - var configuration = provider.GetService(); + var configuration = provider.GetService(); + if (configuration == null) + { + configuration = Configuration; + } + var connectionString = configuration.GetConnectionString("DefaultConnection"); var loggerFactory = LoggerFactory.Create(builder => { @@ -86,5 +132,17 @@ public static DbContextOptionsBuilder CreateEmployeeDbContextOp .UseLoggerFactory(loggerFactory) ; } + + public static T GetInstance() + { + return ServiceProvider.GetService(); + } + + public static void UseMemory() + { + var services = new ServiceCollection(); + services.AddDbContextFactory(ApplyConfigureMemory); + ServiceProvider = services.BuildServiceProvider(); + } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index d5a5db8b..42e8066d 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -4,29 +4,37 @@ using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Lab.DAL { public class EmployeeRepository { - private readonly IDbContextFactory _factory; - - // public EmployeeRepository(IDbContextFactory factory) - // { - // this._factory = factory; - // } - public EmployeeRepository(EmployeeContext factory) + internal IDbContextFactory DbContextFactory { - } - public async Task InsertAsync(InsertRequest request, - string accessId, + get + { + if (this._dbContextFactory == null) + { + this._dbContextFactory = DefaultDbContextFactory.GetInstance>(); + } + + return this._dbContextFactory; + } + set => this._dbContextFactory = value; + } + + private IDbContextFactory _dbContextFactory; + + public async Task InsertAsync(InsertRequest request, + string accessId, CancellationToken cancel = default) { - using var dbContext = this._factory.CreateDbContext(); + using var dbContext = this.DbContextFactory.CreateDbContext(); var id = Guid.NewGuid(); var toDb = new Employee { - Id = id, + Id = id, Name = "yao", Age = 18, }; diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index 4fb1d415..29ca09ce 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -17,5 +17,9 @@ Always - + + + <_Parameter1>Lab.DAL.TestProject + + From bd5cec8250cd0d4708d562ecd04fc8302f279d1d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 12 Apr 2021 09:17:40 +0800 Subject: [PATCH 055/267] add --- .../Lab.Biz/IEmployeeRepository.cs | 8 + .../Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj | 7 + .../Lab.DAL.TestProject.csproj | 33 ++++ .../Lab.DAL.TestProject/UnitTest1.cs | 36 +++++ .../Lab.DAL.TestProject/appsettings.json | 5 + .../Lab.DAL/DefaultDbContextFactory.cs | 142 ++++++++++++++++++ .../DomainModel/Employee/InsertRequest.cs | 24 +++ .../Lab.DAL/EmployeeRepository.cs | 45 ++++++ .../Lab.DAL/EntityModel/Employee.cs | 34 +++++ .../Lab.DAL/EntityModel/EmployeeContext.cs | 52 +++++++ .../Lab.DAL/EntityModel/Identity.cs | 27 ++++ .../Lab.DAL/EntityModel/Order.cs | 24 +++ .../Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 25 +++ .../Lab.VirtualDb/Lab.DAL/appsettings.json | 5 + ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln | 28 ++++ 15 files changed, 495 insertions(+) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/appsettings.json create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs new file mode 100644 index 00000000..5f0cc7f0 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/IEmployeeRepository.cs @@ -0,0 +1,8 @@ +using System; + +namespace Lab.Biz +{ + public interface IEmployeeRepository + { + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj new file mode 100644 index 00000000..cbfa5815 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.Biz/Lab.Biz.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj new file mode 100644 index 00000000..e94f8450 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -0,0 +1,33 @@ + + + + net5.0 + bin + bin\Lab.DAL.TestProject.xml + false + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs new file mode 100644 index 00000000..8787ad2c --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -0,0 +1,36 @@ +using Lab.DAL.DomainModel.Employee; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.DAL.UnitTest +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void 操作真實資料庫() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + var repository = host.Services.GetService(); + var count = repository.InsertAsync(new InsertRequest(), "").Result; + Assert.AreEqual(1, count); + } + + [TestMethod] + public void 操作記憶體() + { + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + + DefaultDbContextFactory.SetUseMemoryDatabase(); + + var repository = host.Services.GetService(); + var count = repository.InsertAsync(new InsertRequest(), "").Result; + Assert.AreEqual(1, count); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/appsettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/appsettings.json new file mode 100644 index 00000000..6e379ce7 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs new file mode 100644 index 00000000..05388ede --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs @@ -0,0 +1,142 @@ +using System; +using System.IO; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.DAL +{ + internal static class DefaultDbContextFactory + { + private static readonly Lazy s_serviceProviderLazy; + private static readonly Lazy s_configurationLazy; + private static ServiceProvider s_serviceProvider; + private static IConfiguration s_configuration; + + public static ServiceProvider ServiceProvider + { + get + { + if (s_serviceProvider == null) + { + s_serviceProvider = s_serviceProviderLazy.Value; + } + + return s_serviceProvider; + } + set => s_serviceProvider = value; + } + + public static IConfiguration Configuration + { + get + { + if (s_configuration == null) + { + s_configuration = s_configurationLazy.Value; + } + + return s_configuration; + } + set => s_configuration = value; + } + + static DefaultDbContextFactory() + { + s_serviceProviderLazy = + new Lazy(() => + { + var services = new ServiceCollection(); + services.AddDbContextFactory(ApplyConfigurePhysical); + return services.BuildServiceProvider(); + }); + s_configurationLazy + = new Lazy(() => + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + return configBuilder.Build(); + }); + } + + public static void ApplyConfigureMemory(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + optionsBuilder.UseInMemoryDatabase("Demo") + .UseLoggerFactory(loggerFactory) + ; + } + + public static void ApplyConfigurePhysical(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + var configuration = provider.GetService(); + if (configuration == null) + { + configuration = Configuration; + } + + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + } + + public static DbContextOptionsBuilder CreateEmployeeDbContextOptionsBuilder() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + return new DbContextOptionsBuilder() + .UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + } + + public static T GetInstance() + { + return ServiceProvider.GetService(); + } + + public static void SetUseMemoryDatabase() + { + var services = new ServiceCollection(); + services.AddDbContextFactory(ApplyConfigureMemory); + ServiceProvider = services.BuildServiceProvider(); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs new file mode 100644 index 00000000..dee69ae8 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Lab.DAL.DomainModel.Employee +{ + public class InsertRequest + { + [Key] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs new file mode 100644 index 00000000..42e8066d --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.DAL +{ + public class EmployeeRepository + { + internal IDbContextFactory DbContextFactory + { + get + { + if (this._dbContextFactory == null) + { + this._dbContextFactory = DefaultDbContextFactory.GetInstance>(); + } + + return this._dbContextFactory; + } + set => this._dbContextFactory = value; + } + + private IDbContextFactory _dbContextFactory; + + public async Task InsertAsync(InsertRequest request, + string accessId, + CancellationToken cancel = default) + { + using var dbContext = this.DbContextFactory.CreateDbContext(); + var id = Guid.NewGuid(); + var toDb = new Employee + { + Id = id, + Name = "yao", + Age = 18, + }; + await dbContext.Employees.AddAsync(toDb, cancel); + return await dbContext.SaveChangesAsync(cancel); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs new file mode 100644 index 00000000..9561c908 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.DAL.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + public virtual Identity Identity { get; set; } + + // public virtual ICollection Order { get; set; } + + public Employee() + { + // this.Order = new HashSet(); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs new file mode 100644 index 00000000..4660cb3f --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -0,0 +1,52 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; + +namespace Lab.DAL.EntityModel +{ + public class EmployeeContext : DbContext + { + private static readonly bool[] s_migrated = {false}; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet Orders { get; set; } + + public string ConnectionString { get; } + + public EmployeeContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0] == false) + { + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + if (memoryOptions == null) + { + this.Database.Migrate(); + } + + s_migrated[0] = true; + } + } + } + } + + // 建構函數配置失敗才需要以下處理 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // var connectionString = + // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; + // + // // var connectionString = this._connectionString; + // if (optionsBuilder.IsConfigured == false) + // { + // optionsBuilder.UseSqlServer(connectionString); + // } + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs new file mode 100644 index 00000000..fcd1d3cb --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.DAL.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid EmployeeId { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs new file mode 100644 index 00000000..aca86633 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.DAL.EntityModel +{ + [Table("Order")] + public class Order + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + public Guid? EmployeeId { get; set; } + + public DateTime? OrderTime { get; set; } + + public string Remark { get; set; } + + public long SequenceId { get; set; } + + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj new file mode 100644 index 00000000..29ca09ce --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -0,0 +1,25 @@ + + + + net5.0 + bin + bin\Lab.DAL.xml + + + + + + + + + + + Always + + + + + <_Parameter1>Lab.DAL.TestProject + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json new file mode 100644 index 00000000..79b166fe --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln new file mode 100644 index 00000000..f401e0a4 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL", "Lab.DAL\Lab.DAL.csproj", "{0258C7C6-B9DE-4050-BE38-D3BE0750BA9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL.TestProject", "Lab.DAL.TestProject\Lab.DAL.TestProject.csproj", "{3DAC8D3D-E494-459B-BEAD-AD306034E441}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Biz", "Lab.Biz\Lab.Biz.csproj", "{0056BEF7-8B47-4387-9110-788A3B73E452}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0258C7C6-B9DE-4050-BE38-D3BE0750BA9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0258C7C6-B9DE-4050-BE38-D3BE0750BA9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0258C7C6-B9DE-4050-BE38-D3BE0750BA9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0258C7C6-B9DE-4050-BE38-D3BE0750BA9D}.Release|Any CPU.Build.0 = Release|Any CPU + {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DAC8D3D-E494-459B-BEAD-AD306034E441}.Release|Any CPU.Build.0 = Release|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 98cf02c4c9ac1de5e4edf19f6a147e9b4585ecf0 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 12 Apr 2021 09:23:18 +0800 Subject: [PATCH 056/267] refactor --- .../Lab.DAL.TestProject/UnitTest1.cs | 70 +------------------ .../Lab.DAL/DefaultDbContextFactory.cs | 41 ----------- .../DomainModel/Employee/InsertRequest.cs | 9 +-- .../Lab.DAL/EntityModel/Employee.cs | 24 ------- .../Lab.DAL/EntityModel/Identity.cs | 11 --- 5 files changed, 2 insertions(+), 153 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index 7fef7ff6..0ff6538c 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -1,13 +1,4 @@ -<<<<<<< HEAD using Lab.DAL.DomainModel.Employee; -======= -using System; -using System.Linq; -using Lab.DAL.DomainModel.Employee; -using Lab.DAL.EntityModel; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Internal; ->>>>>>> origin/master using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,62 +8,17 @@ namespace Lab.DAL.UnitTest [TestClass] public class UnitTest1 { - [TestMethod] -<<<<<<< HEAD public void 操作真實資料庫() { var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); -======= - public void TestMethod1() - { - var options = DefaultDbContextFactory.CreateEmployeeDbContextOptions(); - using (var dbContext = new EmployeeContext(options)) - { - var employees = dbContext.Employees.AsNoTracking().ToList(); - } - } - - [TestMethod] - public void TestMethod2() - { - var options = DefaultDbContextFactory.CreateEmployeeDbContextOptions(); - - using (var dbContext = new EmployeeContext(options)) - { - var id = Guid.NewGuid(); - var toDb = new Employee - { - Id = id, - Name = "yao", - Age = 18, - }; - dbContext.Employees.Add(toDb); - var count = dbContext.SaveChanges(); - Assert.AreEqual(true, count != 0); - Assert.AreEqual(true, toDb.SequenceId != 0); - } - } - - - - [TestMethod] - public void 注入DbContextFactory操作真實資料庫() - { - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => - { - services.AddSingleton(); - }); ->>>>>>> origin/master - var host = builder.Build(); + var host = builder.Build(); var repository = host.Services.GetService(); var count = repository.InsertAsync(new InsertRequest(), "").Result; Assert.AreEqual(1, count); } [TestMethod] -<<<<<<< HEAD public void 操作記憶體() { var builder = Host.CreateDefaultBuilder() @@ -80,20 +26,6 @@ public void 操作記憶體() var host = builder.Build(); DefaultDbContextFactory.SetUseMemoryDatabase(); - -======= - public void 注入DbContextFactory操作記憶體() - { - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => - { - services.AddSingleton(); - }); - var host = builder.Build(); - - DefaultDbContextFactory.UseMemory(); - ->>>>>>> origin/master var repository = host.Services.GetService(); var count = repository.InsertAsync(new InsertRequest(), "").Result; Assert.AreEqual(1, count); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs index 51e12efa..80dea349 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs @@ -8,11 +8,7 @@ namespace Lab.DAL { -<<<<<<< HEAD internal static class DefaultDbContextFactory -======= - public static class DefaultDbContextFactory ->>>>>>> origin/master { private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; @@ -54,10 +50,7 @@ static DefaultDbContextFactory() { var services = new ServiceCollection(); services.AddDbContextFactory(ApplyConfigurePhysical); -<<<<<<< HEAD -======= ->>>>>>> origin/master return services.BuildServiceProvider(); }); s_configurationLazy @@ -113,46 +106,12 @@ public static void ApplyConfigurePhysical(IServiceProvider provider, ; } -<<<<<<< HEAD -======= - public static DbContextOptions CreateEmployeeDbContextOptions() - { - return CreateEmployeeDbContextOptionsBuilder().Options; - } - ->>>>>>> origin/master - public static DbContextOptionsBuilder CreateEmployeeDbContextOptionsBuilder() - { - var configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - var connectionString = configuration.GetConnectionString("DefaultConnection"); - var loggerFactory = LoggerFactory.Create(builder => - { - builder - - //.AddFilter("Microsoft", LogLevel.Warning) - //.AddFilter("System", LogLevel.Warning) - .AddFilter("Lab.DAL", LogLevel.Debug) - .AddConsole() - ; - }); - return new DbContextOptionsBuilder() - .UseSqlServer(connectionString) - .UseLoggerFactory(loggerFactory) - ; - } - public static T GetInstance() { return ServiceProvider.GetService(); } -<<<<<<< HEAD public static void SetUseMemoryDatabase() -======= - public static void UseMemory() ->>>>>>> origin/master { var services = new ServiceCollection(); services.AddDbContextFactory(ApplyConfigureMemory); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs index 265527ed..861980e9 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs @@ -1,5 +1,4 @@ -<<<<<<< HEAD -using System; +using System; using System.ComponentModel.DataAnnotations; namespace Lab.DAL.DomainModel.Employee @@ -21,11 +20,5 @@ public class InsertRequest public string Password { get; set; } public string Remark { get; set; } -======= -namespace Lab.DAL.DomainModel.Employee -{ - public class InsertRequest - { ->>>>>>> origin/master } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs index 64ac474b..01950480 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs @@ -5,24 +5,12 @@ namespace Lab.DAL.EntityModel { - [Table("Employee")] -<<<<<<< HEAD public class Employee { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } - [Required] -======= - - public class Employee - { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Id { get; set; } - ->>>>>>> origin/master public string Name { get; set; } public int? Age { get; set; } @@ -34,19 +22,7 @@ public class Employee public virtual Identity Identity { get; set; } -<<<<<<< HEAD // public virtual ICollection Order { get; set; } - public Employee() - { - // this.Order = new HashSet(); -======= - public virtual ICollection Order { get; set; } - - public Employee() - { - this.Order = new HashSet(); ->>>>>>> origin/master - } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs index 6d73c256..053412c2 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs @@ -8,7 +8,6 @@ namespace Lab.DAL.EntityModel public class Identity { [Key] -<<<<<<< HEAD [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid EmployeeId { get; set; } @@ -18,16 +17,6 @@ public class Identity [Required] public string Password { get; set; } - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] -======= - [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid EmployeeId { get; set; } - - public string Account { get; set; } - - public string Password { get; set; } - ->>>>>>> origin/master public long SequenceId { get; set; } public string Remark { get; set; } From 69c6b41e69e10cc0dc85d5c0d7e0452ec4e5c68b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 12 Apr 2021 10:31:06 +0800 Subject: [PATCH 057/267] refactor --- .../Lab.DAL.TestProject/UnitTest1.cs | 21 ++- ...tFactory.cs => DefaultDbContextManager.cs} | 43 +++--- .../Lab.DAL/EmployeeContextFactory.cs | 29 ++++ .../Lab.DAL/EmployeeRepository.cs | 37 +++-- .../Lab.DAL/EntityModel/EmployeeContext.cs | 3 +- .../Lab.DAL/EntityModel/Identity.cs | 9 +- .../Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 1 + .../20210412020922_InitialCreate.Designer.cs | 126 ++++++++++++++++++ .../20210412020922_InitialCreate.cs | 87 ++++++++++++ .../EmployeeContextModelSnapshot.cs | 124 +++++++++++++++++ 10 files changed, 433 insertions(+), 47 deletions(-) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/{DefaultDbContextFactory.cs => DefaultDbContextManager.cs} (65%) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index 0ff6538c..a78a01d2 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -1,4 +1,6 @@ +using System; using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,14 +10,23 @@ namespace Lab.DAL.UnitTest [TestClass] public class UnitTest1 { + [TestMethod] public void 操作真實資料庫() { var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); - var host = builder.Build(); + var host = builder.Build(); var repository = host.Services.GetService(); - var count = repository.InsertAsync(new InsertRequest(), "").Result; - Assert.AreEqual(1, count); + var count = repository.InsertAsync(new InsertRequest + { + Id = Guid.NewGuid(), + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + Remark = "測試案例,持續航向偉大航道" + }, "").Result; + Assert.AreEqual(2, count); } [TestMethod] @@ -25,10 +36,10 @@ public void 操作記憶體() .ConfigureServices(services => { services.AddSingleton(); }); var host = builder.Build(); - DefaultDbContextFactory.SetUseMemoryDatabase(); + DefaultDbContextManager.SetUseMemoryDatabase(); var repository = host.Services.GetService(); var count = repository.InsertAsync(new InsertRequest(), "").Result; - Assert.AreEqual(1, count); + Assert.AreEqual(2, count); } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs similarity index 65% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 80dea349..fc32fa3a 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextFactory.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -8,12 +8,13 @@ namespace Lab.DAL { - internal static class DefaultDbContextFactory + internal static class DefaultDbContextManager { private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; private static ServiceProvider s_serviceProvider; private static IConfiguration s_configuration; + private static readonly ILoggerFactory s_loggerFactory; public static ServiceProvider ServiceProvider { @@ -43,7 +44,7 @@ public static IConfiguration Configuration set => s_configuration = value; } - static DefaultDbContextFactory() + static DefaultDbContextManager() { s_serviceProviderLazy = new Lazy(() => @@ -61,23 +62,23 @@ static DefaultDbContextFactory() .AddJsonFile("appsettings.json"); return configBuilder.Build(); }); + s_loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); } public static void ApplyConfigureMemory(IServiceProvider provider, DbContextOptionsBuilder optionsBuilder) { - var loggerFactory = LoggerFactory.Create(builder => - { - builder - - //.AddFilter("Microsoft", LogLevel.Warning) - //.AddFilter("System", LogLevel.Warning) - .AddFilter("Lab.DAL", LogLevel.Debug) - .AddConsole() - ; - }); optionsBuilder.UseInMemoryDatabase("Demo") - .UseLoggerFactory(loggerFactory) + .UseLoggerFactory(s_loggerFactory) ; } @@ -91,18 +92,8 @@ public static void ApplyConfigurePhysical(IServiceProvider provider, } var connectionString = configuration.GetConnectionString("DefaultConnection"); - var loggerFactory = LoggerFactory.Create(builder => - { - builder - - //.AddFilter("Microsoft", LogLevel.Warning) - //.AddFilter("System", LogLevel.Warning) - .AddFilter("Lab.DAL", LogLevel.Debug) - .AddConsole() - ; - }); optionsBuilder.UseSqlServer(connectionString) - .UseLoggerFactory(loggerFactory) + .UseLoggerFactory(s_loggerFactory) ; } @@ -111,10 +102,10 @@ public static T GetInstance() return ServiceProvider.GetService(); } - public static void SetUseMemoryDatabase() + public static void SetUseMemoryDatabase() where TContext : DbContext { var services = new ServiceCollection(); - services.AddDbContextFactory(ApplyConfigureMemory); + services.AddDbContextFactory(ApplyConfigureMemory); ServiceProvider = services.BuildServiceProvider(); } } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs new file mode 100644 index 00000000..4609fcd5 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Lab.DAL +{ + public class EmployeeContextFactory : IDesignTimeDbContextFactory + { + public EmployeeContext CreateDbContext(string[] args) + { + Console.WriteLine("由設計工具產生 Database,初始化 DbContextOptionsBuilder"); + + var config = DefaultDbContextManager.Configuration; + var connectionString = config.GetConnectionString("DefaultConnection"); + + Console.WriteLine($"由 appsettings.json 讀取連線字串為:{connectionString}"); + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + Console.WriteLine($"DbContextOptionsBuilder 設定完成"); + + return new EmployeeContext(optionsBuilder.Options); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index 42e8066d..a4ff2405 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -4,7 +4,6 @@ using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace Lab.DAL { @@ -16,7 +15,7 @@ internal IDbContextFactory DbContextFactory { if (this._dbContextFactory == null) { - this._dbContextFactory = DefaultDbContextFactory.GetInstance>(); + this._dbContextFactory = DefaultDbContextManager.GetInstance>(); } return this._dbContextFactory; @@ -30,16 +29,34 @@ public async Task InsertAsync(InsertRequest request, string accessId, CancellationToken cancel = default) { - using var dbContext = this.DbContextFactory.CreateDbContext(); - var id = Guid.NewGuid(); - var toDb = new Employee + await using var dbContext = this.DbContextFactory.CreateDbContext(); + + var id = Guid.NewGuid(); + var toDbEmployee = new Employee + { + Id = id, + Name = request.Name, + Age = request.Age, + Remark = request.Remark, + }; + var toDbIdentity = new Identity { - Id = id, - Name = "yao", - Age = 18, + Account = request.Account, + Password = request.Password, + Remark = request.Remark, + Employee = toDbEmployee }; - await dbContext.Employees.AddAsync(toDb, cancel); - return await dbContext.SaveChangesAsync(cancel); + toDbEmployee.Identity = toDbIdentity; + await dbContext.Employees.AddAsync(toDbEmployee, cancel); + try + { + return await dbContext.SaveChangesAsync(cancel); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 4660cb3f..688a16cc 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -13,8 +13,6 @@ public class EmployeeContext : DbContext public virtual DbSet Orders { get; set; } - public string ConnectionString { get; } - public EmployeeContext(DbContextOptions options) : base(options) { @@ -36,6 +34,7 @@ public EmployeeContext(DbContextOptions options) } } + // 給 Migration CLI 使用 // 建構函數配置失敗才需要以下處理 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs index 053412c2..a38a1890 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs @@ -8,8 +8,8 @@ namespace Lab.DAL.EntityModel public class Identity { [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid EmployeeId { get; set; } + // [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Employee_Id { get; set; } [Required] public string Account { get; set; } @@ -17,10 +17,11 @@ public class Identity [Required] public string Password { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long SequenceId { get; set; } + public string Remark { get; set; } - public string Remark { get; set; } - + [ForeignKey("Employee_Id")] public virtual Employee Employee { get; set; } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index 29ca09ce..dbdf1ac8 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -9,6 +9,7 @@ + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs new file mode 100644 index 00000000..42d61c8b --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs @@ -0,0 +1,126 @@ +// +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Lab.DAL.Migrations +{ + [DbContext(typeof(EmployeeContext))] + [Migration("20210412020922_InitialCreate")] + partial class InitialCreate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Age") + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Id"); + + b.ToTable("Employees"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.Property("Employee_Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Account") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Employee_Id"); + + b.ToTable("Identity"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderTime") + .HasColumnType("datetime2"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithOne("Identity") + .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Navigation("Identity"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs new file mode 100644 index 00000000..41e8f2cb --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs @@ -0,0 +1,87 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Lab.DAL.Migrations +{ + public partial class InitialCreate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Employees", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + Age = table.Column(type: "int", nullable: true), + SequenceId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Remark = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Employees", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Identity", + columns: table => new + { + Employee_Id = table.Column(type: "uniqueidentifier", nullable: false), + Account = table.Column(type: "nvarchar(max)", nullable: false), + Password = table.Column(type: "nvarchar(max)", nullable: false), + SequenceId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Remark = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Identity", x => x.Employee_Id); + table.ForeignKey( + name: "FK_Identity_Employees_Employee_Id", + column: x => x.Employee_Id, + principalTable: "Employees", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + EmployeeId = table.Column(type: "uniqueidentifier", nullable: true), + OrderTime = table.Column(type: "datetime2", nullable: true), + Remark = table.Column(type: "nvarchar(max)", nullable: true), + SequenceId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + table.ForeignKey( + name: "FK_Order_Employees_EmployeeId", + column: x => x.EmployeeId, + principalTable: "Employees", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Order_EmployeeId", + table: "Order", + column: "EmployeeId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Identity"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.DropTable( + name: "Employees"); + } + } +} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs new file mode 100644 index 00000000..4fc5046b --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs @@ -0,0 +1,124 @@ +// +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Lab.DAL.Migrations +{ + [DbContext(typeof(EmployeeContext))] + partial class EmployeeContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Age") + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Id"); + + b.ToTable("Employees"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.Property("Employee_Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Account") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Employee_Id"); + + b.ToTable("Identity"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderTime") + .HasColumnType("datetime2"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithOne("Identity") + .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Navigation("Identity"); + }); +#pragma warning restore 612, 618 + } + } +} From c8b846c597ef9b83e56172a498ede34806959021 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 12 Apr 2021 10:49:30 +0800 Subject: [PATCH 058/267] refactory --- .../Lab.DAL/EntityModel/Employee.cs | 5 ++- .../Lab.DAL/EntityModel/EmployeeContext.cs | 27 ++++++++++++++++ .../Lab.DAL/EntityModel/Identity.cs | 2 +- ... 20210412024817_InitialCreate.Designer.cs} | 19 ++++++++--- ...ate.cs => 20210412024817_InitialCreate.cs} | 32 ++++++++++++++----- .../EmployeeContextModelSnapshot.cs | 17 +++++++--- .../Lab.VirtualDb/Lab.DAL/appsettings.json | 2 +- 7 files changed, 82 insertions(+), 22 deletions(-) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210412020922_InitialCreate.Designer.cs => 20210412024817_InitialCreate.Designer.cs} (88%) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210412020922_InitialCreate.cs => 20210412024817_InitialCreate.cs} (76%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs index 01950480..dde26937 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs @@ -1,14 +1,14 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Lab.DAL.EntityModel { + [Table("Employee")] public class Employee { [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Id { get; set; } public string Name { get; set; } @@ -23,6 +23,5 @@ public class Employee public virtual Identity Identity { get; set; } // public virtual ICollection Order { get; set; } - } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 688a16cc..355cf085 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -13,6 +13,33 @@ public class EmployeeContext : DbContext public virtual DbSet Orders { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + }); + + modelBuilder.Entity(p => + { + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + }); + modelBuilder.Entity(p => + { + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + public EmployeeContext(DbContextOptions options) : base(options) { diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs index a38a1890..d1c1fa9f 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs @@ -8,7 +8,7 @@ namespace Lab.DAL.EntityModel public class Identity { [Key] - // [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Employee_Id { get; set; } [Required] diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs similarity index 88% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs index 42d61c8b..438f07ea 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.Designer.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs @@ -10,7 +10,7 @@ namespace Lab.DAL.Migrations { [DbContext(typeof(EmployeeContext))] - [Migration("20210412020922_InitialCreate")] + [Migration("20210412024817_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,7 +24,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Age") @@ -41,9 +40,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .UseIdentityColumn(); - b.HasKey("Id"); + b.HasKey("Id") + .IsClustered(false); - b.ToTable("Employees"); + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); + + b.ToTable("Employee"); }); modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => @@ -67,7 +71,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .UseIdentityColumn(); - b.HasKey("Employee_Id"); + b.HasKey("Employee_Id") + .IsClustered(false); + + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); b.ToTable("Identity"); }); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs similarity index 76% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs index 41e8f2cb..d4a12d8d 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412020922_InitialCreate.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs @@ -8,7 +8,7 @@ public partial class InitialCreate : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "Employees", + name: "Employee", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), @@ -20,7 +20,8 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_Employees", x => x.Id); + table.PrimaryKey("PK_Employee", x => x.Id) + .Annotation("SqlServer:Clustered", false); }); migrationBuilder.CreateTable( @@ -36,11 +37,12 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_Identity", x => x.Employee_Id); + table.PrimaryKey("PK_Identity", x => x.Employee_Id) + .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_Identity_Employees_Employee_Id", + name: "FK_Identity_Employee_Employee_Id", column: x => x.Employee_Id, - principalTable: "Employees", + principalTable: "Employee", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -59,13 +61,27 @@ protected override void Up(MigrationBuilder migrationBuilder) { table.PrimaryKey("PK_Order", x => x.Id); table.ForeignKey( - name: "FK_Order_Employees_EmployeeId", + name: "FK_Order_Employee_EmployeeId", column: x => x.EmployeeId, - principalTable: "Employees", + principalTable: "Employee", principalColumn: "Id", onDelete: ReferentialAction.Restrict); }); + migrationBuilder.CreateIndex( + name: "IX_Employee_SequenceId", + table: "Employee", + column: "SequenceId", + unique: true) + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_Identity_SequenceId", + table: "Identity", + column: "SequenceId", + unique: true) + .Annotation("SqlServer:Clustered", true); + migrationBuilder.CreateIndex( name: "IX_Order_EmployeeId", table: "Order", @@ -81,7 +97,7 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "Order"); migrationBuilder.DropTable( - name: "Employees"); + name: "Employee"); } } } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs index 4fc5046b..226c6cb2 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs @@ -22,7 +22,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Age") @@ -39,9 +38,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .UseIdentityColumn(); - b.HasKey("Id"); + b.HasKey("Id") + .IsClustered(false); - b.ToTable("Employees"); + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); + + b.ToTable("Employee"); }); modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => @@ -65,7 +69,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .UseIdentityColumn(); - b.HasKey("Employee_Id"); + b.HasKey("Employee_Id") + .IsClustered(false); + + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); b.ToTable("Identity"); }); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json index 79b166fe..0e917009 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json @@ -1,5 +1,5 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL;Trusted_Connection=True;MultipleActiveResultSets=true" + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.Test;Trusted_Connection=True;MultipleActiveResultSets=true" } } \ No newline at end of file From 154ba5dcfec101cc30f66dddb2400d857b7afd11 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 12 Apr 2021 12:09:41 +0800 Subject: [PATCH 059/267] refactor --- .../Lab.DAL.TestProject/UnitTest1.cs | 47 +++++++++++- .../Lab.DAL/DefaultDbContextManager.cs | 34 +++++++-- .../Employee/InsertOrderRequest.cs | 15 ++++ .../{InsertRequest.cs => NewRequest.cs} | 2 +- .../Lab.DAL/EmployeeRepository.cs | 73 +++++++++++++------ .../Lab.DAL/EntityModel/Employee.cs | 9 ++- .../Lab.DAL/EntityModel/EmployeeContext.cs | 2 +- .../Lab.DAL/EntityModel/Identity.cs | 9 ++- .../Lab.DAL/EntityModel/Order.cs | 24 ------ .../Lab.DAL/EntityModel/OrderHistory.cs | 31 ++++++++ ... 20210412040708_InitialCreate.Designer.cs} | 53 +++++++++----- ...ate.cs => 20210412040708_InitialCreate.cs} | 57 +++++++-------- .../EmployeeContextModelSnapshot.cs | 51 ++++++++----- .../Lab.VirtualDb/Lab.DAL/appsettings.json | 2 +- 14 files changed, 283 insertions(+), 126 deletions(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/{InsertRequest.cs => NewRequest.cs} (93%) delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/OrderHistory.cs rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210412024817_InitialCreate.Designer.cs => 20210412040708_InitialCreate.Designer.cs} (73%) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210412024817_InitialCreate.cs => 20210412040708_InitialCreate.cs} (75%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs index a78a01d2..f43a6313 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs @@ -10,14 +10,17 @@ namespace Lab.DAL.UnitTest [TestClass] public class UnitTest1 { + [TestMethod] public void 操作真實資料庫() { + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); var host = builder.Build(); var repository = host.Services.GetService(); - var count = repository.InsertAsync(new InsertRequest + var count = repository.NewAsync(new NewRequest { Id = Guid.NewGuid(), Account = "yao", @@ -25,20 +28,56 @@ public void 操作真實資料庫() Name = "余小章", Age = 18, Remark = "測試案例,持續航向偉大航道" - }, "").Result; + }, "TestUser").Result; Assert.AreEqual(2, count); } + [TestMethod] + public void 操作真實資料庫1() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + var repository = host.Services.GetService(); + var id = Guid.NewGuid(); + repository.NewAsync(new NewRequest + { + Id = id, + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Wait(); + + //act + var count = repository.InsertLogAsync(new InsertOrderRequest + { + Employee_Id = id, + Product_Id = "A001", + Product_Name = "羅技滑鼠", + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Result; + + //assert + Assert.AreEqual(1, count); + } + [TestMethod] public void 操作記憶體() { + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetUseMemoryDatabase(); + var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); var host = builder.Build(); - DefaultDbContextManager.SetUseMemoryDatabase(); var repository = host.Services.GetService(); - var count = repository.InsertAsync(new InsertRequest(), "").Result; + var count = repository.NewAsync(new NewRequest(), "TestUser").Result; Assert.AreEqual(2, count); } } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index fc32fa3a..006e4d88 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -8,13 +8,28 @@ namespace Lab.DAL { - internal static class DefaultDbContextManager + internal class DefaultDbContextManager { private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; + private static readonly ILoggerFactory s_loggerFactory; private static ServiceProvider s_serviceProvider; private static IConfiguration s_configuration; - private static readonly ILoggerFactory s_loggerFactory; + private static DateTime? s_now; + + public static DateTime Now + { + get + { + if (s_now == null) + { + return DateTime.UtcNow; + } + + return s_now.Value; + } + set => s_now = value; + } public static ServiceProvider ServiceProvider { @@ -85,13 +100,13 @@ public static void ApplyConfigureMemory(IServiceProvider provider, public static void ApplyConfigurePhysical(IServiceProvider provider, DbContextOptionsBuilder optionsBuilder) { - var configuration = provider.GetService(); - if (configuration == null) + var config = provider.GetService(); + if (config == null) { - configuration = Configuration; + config = Configuration; } - var connectionString = configuration.GetConnectionString("DefaultConnection"); + var connectionString = config.GetConnectionString("DefaultConnection"); optionsBuilder.UseSqlServer(connectionString) .UseLoggerFactory(s_loggerFactory) ; @@ -102,6 +117,13 @@ public static T GetInstance() return ServiceProvider.GetService(); } + public static void SetUseDefaultDatabase() where TContext : DbContext + { + var services = new ServiceCollection(); + services.AddDbContextFactory(ApplyConfigurePhysical); + ServiceProvider = services.BuildServiceProvider(); + } + public static void SetUseMemoryDatabase() where TContext : DbContext { var services = new ServiceCollection(); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs new file mode 100644 index 00000000..76eb01d6 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs @@ -0,0 +1,15 @@ +using System; + +namespace Lab.DAL.DomainModel.Employee +{ + public class InsertOrderRequest + { + public Guid? Employee_Id { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs similarity index 93% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs index 861980e9..3288a612 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/InsertRequest.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs @@ -3,7 +3,7 @@ namespace Lab.DAL.DomainModel.Employee { - public class InsertRequest + public class NewRequest { [Key] public Guid Id { get; set; } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index a4ff2405..cbea1ae7 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -23,40 +23,71 @@ internal IDbContextFactory DbContextFactory set => this._dbContextFactory = value; } + internal DateTime Now + { + get + { + if (this._now == null) + { + return DefaultDbContextManager.Now; + } + + return this._now.Value; + } + set => this._now = value; + } + private IDbContextFactory _dbContextFactory; + private DateTime? _now; + + public async Task InsertLogAsync(InsertOrderRequest request, + string accessId, + CancellationToken cancel = default) + { + await using var dbContext = this.DbContextFactory.CreateDbContext(); + + var toDbOrderHistory = new OrderHistory + { + Employee_Id = request.Employee_Id, + Product_Id = request.Product_Id, + Product_Name = request.Product_Id, + CreateAt = this.Now, + CreateBy = accessId, + Remark = request.Remark, + }; + + await dbContext.OrderHistories.AddAsync(toDbOrderHistory, cancel); + return await dbContext.SaveChangesAsync(cancel); + } - public async Task InsertAsync(InsertRequest request, - string accessId, - CancellationToken cancel = default) + public async Task NewAsync(NewRequest request, + string accessId, + CancellationToken cancel = default) { await using var dbContext = this.DbContextFactory.CreateDbContext(); var id = Guid.NewGuid(); - var toDbEmployee = new Employee + var employeeToDb = new Employee { - Id = id, - Name = request.Name, - Age = request.Age, - Remark = request.Remark, + Id = id, + Name = request.Name, + Age = request.Age, + Remark = request.Remark, + CreateAt = this.Now, + CreateBy = accessId }; - var toDbIdentity = new Identity + var identityToDb = new Identity { Account = request.Account, Password = request.Password, Remark = request.Remark, - Employee = toDbEmployee + Employee = employeeToDb, + CreateAt = this.Now, + CreateBy = accessId }; - toDbEmployee.Identity = toDbIdentity; - await dbContext.Employees.AddAsync(toDbEmployee, cancel); - try - { - return await dbContext.SaveChangesAsync(cancel); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } + employeeToDb.Identity = identityToDb; + await dbContext.Employees.AddAsync(employeeToDb, cancel); + return await dbContext.SaveChangesAsync(cancel); } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs index dde26937..2c375566 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Employee.cs @@ -11,6 +11,7 @@ public class Employee [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Id { get; set; } + [Required] public string Name { get; set; } public int? Age { get; set; } @@ -20,8 +21,12 @@ public class Employee public string Remark { get; set; } - public virtual Identity Identity { get; set; } + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } - // public virtual ICollection Order { get; set; } + public virtual Identity Identity { get; set; } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 355cf085..11f97a07 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -11,7 +11,7 @@ public class EmployeeContext : DbContext public virtual DbSet Identities { get; set; } - public virtual DbSet Orders { get; set; } + public virtual DbSet OrderHistories {get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs index d1c1fa9f..472f9bcb 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Identity.cs @@ -19,7 +19,14 @@ public class Identity [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long SequenceId { get; set; } - public string Remark { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } [ForeignKey("Employee_Id")] public virtual Employee Employee { get; set; } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs deleted file mode 100644 index aca86633..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/Order.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Lab.DAL.EntityModel -{ - [Table("Order")] - public class Order - { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Id { get; set; } - - public Guid? EmployeeId { get; set; } - - public DateTime? OrderTime { get; set; } - - public string Remark { get; set; } - - public long SequenceId { get; set; } - - public virtual Employee Employee { get; set; } - } -} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/OrderHistory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..ca49d134 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/OrderHistory.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.DAL.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs similarity index 73% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs index 438f07ea..ef3fc502 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.Designer.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs @@ -10,7 +10,7 @@ namespace Lab.DAL.Migrations { [DbContext(typeof(EmployeeContext))] - [Migration("20210412024817_InitialCreate")] + [Migration("20210412040708_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -29,7 +29,15 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Age") .HasColumnType("int"); + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Name") + .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("Remark") @@ -59,6 +67,13 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Password") .IsRequired() .HasColumnType("nvarchar(max)"); @@ -81,28 +96,39 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Identity"); }); - modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("EmployeeId") + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Employee_Id") .HasColumnType("uniqueidentifier"); - b.Property("OrderTime") - .HasColumnType("datetime2"); + b.Property("Product_Id") + .HasColumnType("nvarchar(max)"); + + b.Property("Product_Name") + .HasColumnType("nvarchar(max)"); b.Property("Remark") .HasColumnType("nvarchar(max)"); b.Property("SequenceId") - .HasColumnType("bigint"); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); b.HasKey("Id"); - b.HasIndex("EmployeeId"); - - b.ToTable("Order"); + b.ToTable("OrderHistory"); }); modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => @@ -116,15 +142,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Employee"); }); - modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => - { - b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId"); - - b.Navigation("Employee"); - }); - modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => { b.Navigation("Identity"); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.cs similarity index 75% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.cs index d4a12d8d..44edf0c5 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412024817_InitialCreate.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.cs @@ -12,11 +12,13 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(max)", nullable: true), + Name = table.Column(type: "nvarchar(max)", nullable: false), Age = table.Column(type: "int", nullable: true), SequenceId = table.Column(type: "bigint", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - Remark = table.Column(type: "nvarchar(max)", nullable: true) + Remark = table.Column(type: "nvarchar(max)", nullable: true), + CreateAt = table.Column(type: "datetime2", nullable: false), + CreateBy = table.Column(type: "nvarchar(max)", nullable: false) }, constraints: table => { @@ -25,47 +27,47 @@ protected override void Up(MigrationBuilder migrationBuilder) }); migrationBuilder.CreateTable( - name: "Identity", + name: "OrderHistory", columns: table => new { - Employee_Id = table.Column(type: "uniqueidentifier", nullable: false), - Account = table.Column(type: "nvarchar(max)", nullable: false), - Password = table.Column(type: "nvarchar(max)", nullable: false), + Id = table.Column(type: "uniqueidentifier", nullable: false), + Employee_Id = table.Column(type: "uniqueidentifier", nullable: true), + Remark = table.Column(type: "nvarchar(max)", nullable: true), SequenceId = table.Column(type: "bigint", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - Remark = table.Column(type: "nvarchar(max)", nullable: true) + Product_Id = table.Column(type: "nvarchar(max)", nullable: true), + Product_Name = table.Column(type: "nvarchar(max)", nullable: true), + CreateAt = table.Column(type: "datetime2", nullable: false), + CreateBy = table.Column(type: "nvarchar(max)", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Identity", x => x.Employee_Id) - .Annotation("SqlServer:Clustered", false); - table.ForeignKey( - name: "FK_Identity_Employee_Employee_Id", - column: x => x.Employee_Id, - principalTable: "Employee", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + table.PrimaryKey("PK_OrderHistory", x => x.Id); }); migrationBuilder.CreateTable( - name: "Order", + name: "Identity", columns: table => new { - Id = table.Column(type: "uniqueidentifier", nullable: false), - EmployeeId = table.Column(type: "uniqueidentifier", nullable: true), - OrderTime = table.Column(type: "datetime2", nullable: true), - Remark = table.Column(type: "nvarchar(max)", nullable: true), + Employee_Id = table.Column(type: "uniqueidentifier", nullable: false), + Account = table.Column(type: "nvarchar(max)", nullable: false), + Password = table.Column(type: "nvarchar(max)", nullable: false), SequenceId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Remark = table.Column(type: "nvarchar(max)", nullable: true), + CreateAt = table.Column(type: "datetime2", nullable: false), + CreateBy = table.Column(type: "nvarchar(max)", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Order", x => x.Id); + table.PrimaryKey("PK_Identity", x => x.Employee_Id) + .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_Order_Employee_EmployeeId", - column: x => x.EmployeeId, + name: "FK_Identity_Employee_Employee_Id", + column: x => x.Employee_Id, principalTable: "Employee", principalColumn: "Id", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( @@ -81,11 +83,6 @@ protected override void Up(MigrationBuilder migrationBuilder) column: "SequenceId", unique: true) .Annotation("SqlServer:Clustered", true); - - migrationBuilder.CreateIndex( - name: "IX_Order_EmployeeId", - table: "Order", - column: "EmployeeId"); } protected override void Down(MigrationBuilder migrationBuilder) @@ -94,7 +91,7 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "Identity"); migrationBuilder.DropTable( - name: "Order"); + name: "OrderHistory"); migrationBuilder.DropTable( name: "Employee"); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs index 226c6cb2..995a5f12 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs @@ -27,7 +27,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Age") .HasColumnType("int"); + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Name") + .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("Remark") @@ -57,6 +65,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Password") .IsRequired() .HasColumnType("nvarchar(max)"); @@ -79,28 +94,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Identity"); }); - modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => + modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("EmployeeId") + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Employee_Id") .HasColumnType("uniqueidentifier"); - b.Property("OrderTime") - .HasColumnType("datetime2"); + b.Property("Product_Id") + .HasColumnType("nvarchar(max)"); + + b.Property("Product_Name") + .HasColumnType("nvarchar(max)"); b.Property("Remark") .HasColumnType("nvarchar(max)"); b.Property("SequenceId") - .HasColumnType("bigint"); + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); b.HasKey("Id"); - b.HasIndex("EmployeeId"); - - b.ToTable("Order"); + b.ToTable("OrderHistory"); }); modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => @@ -114,15 +140,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Employee"); }); - modelBuilder.Entity("Lab.DAL.EntityModel.Order", b => - { - b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId"); - - b.Navigation("Employee"); - }); - modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => { b.Navigation("Identity"); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json index 0e917009..79b166fe 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/appsettings.json @@ -1,5 +1,5 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.Test;Trusted_Connection=True;MultipleActiveResultSets=true" + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL;Trusted_Connection=True;MultipleActiveResultSets=true" } } \ No newline at end of file From d35db10ace2811312d511676f7af85077aef122b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 14 Apr 2021 11:47:35 +0800 Subject: [PATCH 060/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 197 ++++++++++++++++++ .../Lab.DAL.TestProject.csproj | 1 + .../Lab.DAL.TestProject/UnitTest1.cs | 84 -------- .../Lab.DAL/DefaultDbContextManager.cs | 3 +- .../Lab.DAL/EmployeeRepository.cs | 2 + .../Lab.DAL/EntityModel/EmployeeContext.cs | 76 +++---- 6 files changed, 240 insertions(+), 123 deletions(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs new file mode 100644 index 00000000..7134e185 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -0,0 +1,197 @@ +using System; +using System.IO; +using System.Linq; +using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.DAL.UnitTest +{ + [TestClass] + public class EmployeeRepositoryUnitTests + { + private static readonly DbContextOptions s_employeeContextOptions; + + static EmployeeRepositoryUnitTests() + { + s_employeeContextOptions = CreateDbContextOptions(); + } + + [AssemblyCleanup] + public static void AssemblyCleanup() + { + //刪除測試資料庫 + Console.WriteLine("AssemblyCleanup"); + + using var db = new EmployeeContext(s_employeeContextOptions); + db.Database.EnsureDeleted(); + } + + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + { + //刪除測試資料庫 + Console.WriteLine("AssemblyInitialize"); + using var db = new EmployeeContext(s_employeeContextOptions); + db.Database.EnsureDeleted(); + + //建立測試資料庫 + db.Database.EnsureCreated(); + } + + [ClassCleanup] + public static void ClassCleanup() + { + //刪除測試資料表 + Console.WriteLine("ClassCleanup"); + DeleteTestDataRow(); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + //刪除測試資料表 + Console.WriteLine("ClassInitialize"); + DeleteTestDataRow(); + } + + [TestMethod] + public void 操作真實資料庫_手動取得執行個體() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + var repository = new EmployeeRepository(); + var id = Guid.NewGuid(); + repository.NewAsync(new NewRequest + { + Id = id, + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Wait(); + + //act + var count = repository.InsertLogAsync(new InsertOrderRequest + { + Employee_Id = id, + Product_Id = "A001", + Product_Name = "羅技滑鼠", + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Result; + + //assert + Assert.AreEqual(1, count); + } + + [TestMethod] + public void 操作真實資料庫_從容器取得執行個體() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + + var repository = host.Services.GetService(); + var id = Guid.NewGuid(); + + //act + var count = repository.NewAsync(new NewRequest + { + Id = id, + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + }, "TestUser").Result; + + //assert + Assert.AreEqual(2, count); + var db = new EmployeeContext(s_employeeContextOptions); + + // var actual = db.Employees.FirstOrDefault(); + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + } + + [TestMethod] + public void 操作記憶體() + { + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetUseMemoryDatabase(); + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + + var repository = host.Services.GetService(); + var count = repository.NewAsync(new NewRequest(), "TestUser").Result; + Assert.AreEqual(2, count); + } + + private static DbContextOptions CreateDbContextOptions() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + + var configRoot = configBuilder.Build(); + var connectionString = configRoot.GetConnectionString("DefaultConnection"); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + return new DbContextOptionsBuilder() + .UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + .Options; + } + + private static void DeleteTestDataRow() + { + var dbContextOptions = s_employeeContextOptions; + using var db = new EmployeeContext(dbContextOptions); + var deleteCommand = GetDeleteAllRecordCommand(); + db.Database.ExecuteSqlRaw(deleteCommand); + } + + private static string GetDeleteAllRecordCommand() + { + var sql = @" +-- disable referential integrity +EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL' + + +EXEC sp_MSForEachTable 'DELETE FROM ?' + + +-- enable referential integrity again +EXEC sp_MSForEachTable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL' +"; + + return sql; + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index e94f8450..04554d62 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -8,6 +8,7 @@ + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs deleted file mode 100644 index f43a6313..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/UnitTest1.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using Lab.DAL.DomainModel.Employee; -using Lab.DAL.EntityModel; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Lab.DAL.UnitTest -{ - [TestClass] - public class UnitTest1 - { - - [TestMethod] - public void 操作真實資料庫() - { - DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => { services.AddSingleton(); }); - var host = builder.Build(); - var repository = host.Services.GetService(); - var count = repository.NewAsync(new NewRequest - { - Id = Guid.NewGuid(), - Account = "yao", - Password = "123456", - Name = "余小章", - Age = 18, - Remark = "測試案例,持續航向偉大航道" - }, "TestUser").Result; - Assert.AreEqual(2, count); - } - - [TestMethod] - public void 操作真實資料庫1() - { - //arrange - DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => { services.AddSingleton(); }); - var host = builder.Build(); - var repository = host.Services.GetService(); - var id = Guid.NewGuid(); - repository.NewAsync(new NewRequest - { - Id = id, - Account = "yao", - Password = "123456", - Name = "余小章", - Age = 18, - Remark = "測試案例,持續航向偉大航道" - }, "TestUser").Wait(); - - //act - var count = repository.InsertLogAsync(new InsertOrderRequest - { - Employee_Id = id, - Product_Id = "A001", - Product_Name = "羅技滑鼠", - Remark = "測試案例,持續航向偉大航道" - }, "TestUser").Result; - - //assert - Assert.AreEqual(1, count); - } - - [TestMethod] - public void 操作記憶體() - { - DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - DefaultDbContextManager.SetUseMemoryDatabase(); - - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => { services.AddSingleton(); }); - var host = builder.Build(); - - var repository = host.Services.GetService(); - var count = repository.NewAsync(new NewRequest(), "TestUser").Result; - Assert.AreEqual(2, count); - } - } -} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 006e4d88..82237866 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -10,6 +10,7 @@ namespace Lab.DAL { internal class DefaultDbContextManager { + public static readonly bool[] Migrated = {false}; private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; private static readonly ILoggerFactory s_loggerFactory; @@ -116,7 +117,7 @@ public static T GetInstance() { return ServiceProvider.GetService(); } - + public static void SetUseDefaultDatabase() where TContext : DbContext { var services = new ServiceCollection(); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index cbea1ae7..bb49074f 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -76,6 +76,7 @@ public async Task NewAsync(NewRequest request, CreateAt = this.Now, CreateBy = accessId }; + var identityToDb = new Identity { Account = request.Account, @@ -85,6 +86,7 @@ public async Task NewAsync(NewRequest request, CreateAt = this.Now, CreateBy = accessId }; + employeeToDb.Identity = identityToDb; await dbContext.Employees.AddAsync(employeeToDb, cancel); return await dbContext.SaveChangesAsync(cancel); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs index 11f97a07..88cf3a78 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs @@ -5,13 +5,48 @@ namespace Lab.DAL.EntityModel { public class EmployeeContext : DbContext { - private static readonly bool[] s_migrated = {false}; - public virtual DbSet Employees { get; set; } public virtual DbSet Identities { get; set; } - public virtual DbSet OrderHistories {get; set; } + public virtual DbSet OrderHistories { get; set; } + + public EmployeeContext(DbContextOptions options) + : base(options) + { + if (DefaultDbContextManager.Migrated[0]) + { + return; + } + + lock (DefaultDbContextManager.Migrated) + { + if (DefaultDbContextManager.Migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + if (memoryOptions == null) + { + this.Database.Migrate(); + } + + DefaultDbContextManager.Migrated[0] = true; + } + } + } + + // 給 Migration CLI 使用 + // 建構函數配置失敗才需要以下處理 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // var connectionString = + // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; + // + // // var connectionString = this._connectionString; + // if (optionsBuilder.IsConfigured == false) + // { + // optionsBuilder.UseSqlServer(connectionString); + // } + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -39,40 +74,5 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsClustered(); }); } - - public EmployeeContext(DbContextOptions options) - : base(options) - { - if (s_migrated[0] == false) - { - lock (s_migrated) - { - if (s_migrated[0] == false) - { - var memoryOptions = options.FindExtension(); - if (memoryOptions == null) - { - this.Database.Migrate(); - } - - s_migrated[0] = true; - } - } - } - } - - // 給 Migration CLI 使用 - // 建構函數配置失敗才需要以下處理 - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - // var connectionString = - // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; - // - // // var connectionString = this._connectionString; - // if (optionsBuilder.IsConfigured == false) - // { - // optionsBuilder.UseSqlServer(connectionString); - // } - } } } \ No newline at end of file From 2123ba57c71e05436027b210c55acf5319cce830 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Wed, 14 Apr 2021 13:08:21 +0800 Subject: [PATCH 061/267] refactor --- .../Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 1 - .../20210412040708_InitialCreate.Designer.cs | 152 ------------------ ...ate.cs => 20210414050400_InitialCreate.cs} | 0 3 files changed, 153 deletions(-) delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210412040708_InitialCreate.cs => 20210414050400_InitialCreate.cs} (100%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index dbdf1ac8..bdc54140 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -8,7 +8,6 @@ - diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs deleted file mode 100644 index ef3fc502..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.Designer.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -using System; -using Lab.DAL.EntityModel; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Lab.DAL.Migrations -{ - [DbContext(typeof(EmployeeContext))] - [Migration("20210412040708_InitialCreate")] - partial class InitialCreate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.0"); - - modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Age") - .HasColumnType("int"); - - b.Property("CreateAt") - .HasColumnType("datetime2"); - - b.Property("CreateBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Remark") - .HasColumnType("nvarchar(max)"); - - b.Property("SequenceId") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .UseIdentityColumn(); - - b.HasKey("Id") - .IsClustered(false); - - b.HasIndex("SequenceId") - .IsUnique() - .IsClustered(); - - b.ToTable("Employee"); - }); - - modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => - { - b.Property("Employee_Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Account") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreateAt") - .HasColumnType("datetime2"); - - b.Property("CreateBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Password") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Remark") - .HasColumnType("nvarchar(max)"); - - b.Property("SequenceId") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .UseIdentityColumn(); - - b.HasKey("Employee_Id") - .IsClustered(false); - - b.HasIndex("SequenceId") - .IsUnique() - .IsClustered(); - - b.ToTable("Identity"); - }); - - modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreateAt") - .HasColumnType("datetime2"); - - b.Property("CreateBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Employee_Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Product_Id") - .HasColumnType("nvarchar(max)"); - - b.Property("Product_Name") - .HasColumnType("nvarchar(max)"); - - b.Property("Remark") - .HasColumnType("nvarchar(max)"); - - b.Property("SequenceId") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .UseIdentityColumn(); - - b.HasKey("Id"); - - b.ToTable("OrderHistory"); - }); - - modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => - { - b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") - .WithOne("Identity") - .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Employee"); - }); - - modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => - { - b.Navigation("Identity"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210414050400_InitialCreate.cs similarity index 100% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210412040708_InitialCreate.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210414050400_InitialCreate.cs From 7b055dcf089ccb1947317e776b20426927206b32 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 08:57:47 +0800 Subject: [PATCH 062/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 16 ++++++++-------- .../Lab.DAL/DefaultDbContextManager.cs | 2 +- .../Lab.DAL/EmployeeContextFactory.cs | 8 ++++---- .../Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs | 6 +++--- .../{EmployeeContext.cs => EmployeeDbContext.cs} | 4 ++-- .../Migrations/EmployeeContextModelSnapshot.cs | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/{EmployeeContext.cs => EmployeeDbContext.cs} (95%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 7134e185..16808115 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -15,7 +15,7 @@ namespace Lab.DAL.UnitTest [TestClass] public class EmployeeRepositoryUnitTests { - private static readonly DbContextOptions s_employeeContextOptions; + private static readonly DbContextOptions s_employeeContextOptions; static EmployeeRepositoryUnitTests() { @@ -28,7 +28,7 @@ public static void AssemblyCleanup() //刪除測試資料庫 Console.WriteLine("AssemblyCleanup"); - using var db = new EmployeeContext(s_employeeContextOptions); + using var db = new EmployeeDbContext(s_employeeContextOptions); db.Database.EnsureDeleted(); } @@ -37,7 +37,7 @@ public static void AssemblyInitialize(TestContext context) { //刪除測試資料庫 Console.WriteLine("AssemblyInitialize"); - using var db = new EmployeeContext(s_employeeContextOptions); + using var db = new EmployeeDbContext(s_employeeContextOptions); db.Database.EnsureDeleted(); //建立測試資料庫 @@ -115,7 +115,7 @@ public void 操作真實資料庫_從容器取得執行個體() //assert Assert.AreEqual(2, count); - var db = new EmployeeContext(s_employeeContextOptions); + var db = new EmployeeDbContext(s_employeeContextOptions); // var actual = db.Employees.FirstOrDefault(); var actual = db.Employees @@ -133,7 +133,7 @@ public void 操作真實資料庫_從容器取得執行個體() public void 操作記憶體() { DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - DefaultDbContextManager.SetUseMemoryDatabase(); + DefaultDbContextManager.SetUseMemoryDatabase(); var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); @@ -144,7 +144,7 @@ public void 操作記憶體() Assert.AreEqual(2, count); } - private static DbContextOptions CreateDbContextOptions() + private static DbContextOptions CreateDbContextOptions() { var configBuilder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -163,7 +163,7 @@ private static DbContextOptions CreateDbContextOptions() .AddConsole() ; }); - return new DbContextOptionsBuilder() + return new DbContextOptionsBuilder() .UseSqlServer(connectionString) .UseLoggerFactory(loggerFactory) .Options; @@ -172,7 +172,7 @@ private static DbContextOptions CreateDbContextOptions() private static void DeleteTestDataRow() { var dbContextOptions = s_employeeContextOptions; - using var db = new EmployeeContext(dbContextOptions); + using var db = new EmployeeDbContext(dbContextOptions); var deleteCommand = GetDeleteAllRecordCommand(); db.Database.ExecuteSqlRaw(deleteCommand); } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 82237866..38c2be1c 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -66,7 +66,7 @@ static DefaultDbContextManager() new Lazy(() => { var services = new ServiceCollection(); - services.AddDbContextFactory(ApplyConfigurePhysical); + services.AddDbContextFactory(ApplyConfigurePhysical); return services.BuildServiceProvider(); }); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs index 4609fcd5..58195dd9 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeContextFactory.cs @@ -8,9 +8,9 @@ namespace Lab.DAL { - public class EmployeeContextFactory : IDesignTimeDbContextFactory + public class EmployeeContextFactory : IDesignTimeDbContextFactory { - public EmployeeContext CreateDbContext(string[] args) + public EmployeeDbContext CreateDbContext(string[] args) { Console.WriteLine("由設計工具產生 Database,初始化 DbContextOptionsBuilder"); @@ -19,11 +19,11 @@ public EmployeeContext CreateDbContext(string[] args) Console.WriteLine($"由 appsettings.json 讀取連線字串為:{connectionString}"); - var optionsBuilder = new DbContextOptionsBuilder(); + var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlServer(connectionString); Console.WriteLine($"DbContextOptionsBuilder 設定完成"); - return new EmployeeContext(optionsBuilder.Options); + return new EmployeeDbContext(optionsBuilder.Options); } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index bb49074f..e0fef74c 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -9,13 +9,13 @@ namespace Lab.DAL { public class EmployeeRepository { - internal IDbContextFactory DbContextFactory + internal IDbContextFactory DbContextFactory { get { if (this._dbContextFactory == null) { - this._dbContextFactory = DefaultDbContextManager.GetInstance>(); + this._dbContextFactory = DefaultDbContextManager.GetInstance>(); } return this._dbContextFactory; @@ -37,7 +37,7 @@ internal DateTime Now set => this._now = value; } - private IDbContextFactory _dbContextFactory; + private IDbContextFactory _dbContextFactory; private DateTime? _now; public async Task InsertLogAsync(InsertOrderRequest request, diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs similarity index 95% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs index 88cf3a78..8dfebd40 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -3,7 +3,7 @@ namespace Lab.DAL.EntityModel { - public class EmployeeContext : DbContext + public class EmployeeDbContext : DbContext { public virtual DbSet Employees { get; set; } @@ -11,7 +11,7 @@ public class EmployeeContext : DbContext public virtual DbSet OrderHistories { get; set; } - public EmployeeContext(DbContextOptions options) + public EmployeeDbContext(DbContextOptions options) : base(options) { if (DefaultDbContextManager.Migrated[0]) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs index 995a5f12..23f95f8e 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs @@ -8,7 +8,7 @@ namespace Lab.DAL.Migrations { - [DbContext(typeof(EmployeeContext))] + [DbContext(typeof(EmployeeDbContext))] partial class EmployeeContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) From 6fa0e35838b00870435af18fe393b041b0d5422f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 09:41:21 +0800 Subject: [PATCH 063/267] refactor --- .../Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs index 8dfebd40..32c415b6 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -48,6 +48,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) // } } + //管理索引 protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity(p => From 56a8c7bd22604005555cd0b36a4f5dfe22a96e56 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 09:56:30 +0800 Subject: [PATCH 064/267] refactor --- .../20210415015614_InitialCreate.Designer.cs | 152 ++++++++++++++++++ ...ate.cs => 20210415015614_InitialCreate.cs} | 0 ...t.cs => EmployeeDbContextModelSnapshot.cs} | 2 +- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.Designer.cs rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{20210414050400_InitialCreate.cs => 20210415015614_InitialCreate.cs} (100%) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/{EmployeeContextModelSnapshot.cs => EmployeeDbContextModelSnapshot.cs} (98%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.Designer.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.Designer.cs new file mode 100644 index 00000000..da6dc416 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.Designer.cs @@ -0,0 +1,152 @@ +// +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Lab.DAL.Migrations +{ + [DbContext(typeof(EmployeeDbContext))] + [Migration("20210415015614_InitialCreate")] + partial class InitialCreate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Age") + .HasColumnType("int"); + + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Id") + .IsClustered(false); + + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); + + b.ToTable("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.Property("Employee_Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Account") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Employee_Id") + .IsClustered(false); + + b.HasIndex("SequenceId") + .IsUnique() + .IsClustered(); + + b.ToTable("Identity"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreateAt") + .HasColumnType("datetime2"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Employee_Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Product_Id") + .HasColumnType("nvarchar(max)"); + + b.Property("Product_Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Remark") + .HasColumnType("nvarchar(max)"); + + b.Property("SequenceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .UseIdentityColumn(); + + b.HasKey("Id"); + + b.ToTable("OrderHistory"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithOne("Identity") + .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Navigation("Identity"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210414050400_InitialCreate.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.cs similarity index 100% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210414050400_InitialCreate.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/20210415015614_InitialCreate.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs similarity index 98% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs index 23f95f8e..d9b6dd49 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeContextModelSnapshot.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs @@ -9,7 +9,7 @@ namespace Lab.DAL.Migrations { [DbContext(typeof(EmployeeDbContext))] - partial class EmployeeContextModelSnapshot : ModelSnapshot + partial class EmployeeDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { From 2f3bf4b1046668fdf2b11936a9ebadd05ccf62a9 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 11:23:02 +0800 Subject: [PATCH 065/267] refactor --- .../Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs | 4 ++-- .../Lab.DAL.TestProject/Lab.DAL.TestProject.csproj | 8 +++----- ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 16808115..f9a2f333 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -61,7 +61,7 @@ public static void ClassInitialize(TestContext context) } [TestMethod] - public void 操作真實資料庫_手動取得執行個體() + public void 操作真實資料庫_手動取得Repository執行個體() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); @@ -91,7 +91,7 @@ public void 操作真實資料庫_手動取得執行個體() } [TestMethod] - public void 操作真實資料庫_從容器取得執行個體() + public void 操作真實資料庫_從容器取得Repository執行個體() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index 04554d62..d1e58815 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -14,11 +14,9 @@ - - - - - + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index bdc54140..d5ddf873 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -6,9 +6,9 @@ bin\Lab.DAL.xml - - - + + + From a531fee809587d071dc009e8f8e219df76b3516f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 11:55:50 +0800 Subject: [PATCH 066/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 3 ++- .../Lab.DAL/DefaultDbContextManager.cs | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index f9a2f333..b079931d 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -95,6 +95,7 @@ public void 操作真實資料庫_從容器取得Repository執行個體() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetPhysicalDatabase(); var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); @@ -133,7 +134,7 @@ public void 操作真實資料庫_從容器取得Repository執行個體() public void 操作記憶體() { DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - DefaultDbContextManager.SetUseMemoryDatabase(); + DefaultDbContextManager.SetMemoryDatabase(); var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton(); }); diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 38c2be1c..a2bc9b3e 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -60,14 +60,16 @@ public static IConfiguration Configuration set => s_configuration = value; } + private static ServiceCollection s_services; static DefaultDbContextManager() { + s_services = new ServiceCollection(); + s_serviceProviderLazy = new Lazy(() => { - var services = new ServiceCollection(); + var services = s_services; services.AddDbContextFactory(ApplyConfigurePhysical); - return services.BuildServiceProvider(); }); s_configurationLazy @@ -90,16 +92,16 @@ static DefaultDbContextManager() }); } - public static void ApplyConfigureMemory(IServiceProvider provider, - DbContextOptionsBuilder optionsBuilder) + private static void ApplyConfigureMemory(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase("Demo") .UseLoggerFactory(s_loggerFactory) ; } - public static void ApplyConfigurePhysical(IServiceProvider provider, - DbContextOptionsBuilder optionsBuilder) + private static void ApplyConfigurePhysical(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) { var config = provider.GetService(); if (config == null) @@ -118,16 +120,16 @@ public static T GetInstance() return ServiceProvider.GetService(); } - public static void SetUseDefaultDatabase() where TContext : DbContext + public static void SetPhysicalDatabase() where TContext : DbContext { - var services = new ServiceCollection(); + var services = s_services; services.AddDbContextFactory(ApplyConfigurePhysical); ServiceProvider = services.BuildServiceProvider(); } - public static void SetUseMemoryDatabase() where TContext : DbContext + public static void SetMemoryDatabase() where TContext : DbContext { - var services = new ServiceCollection(); + var services = s_services; services.AddDbContextFactory(ApplyConfigureMemory); ServiceProvider = services.BuildServiceProvider(); } From 4a88ffaa26d5378016cb8829f3ef4c24a5e4070b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 15:44:08 +0800 Subject: [PATCH 067/267] refactor --- .../Lab.DAL.InMemoryTestProject.csproj | 16 +++++ .../Lab.DAL.InMemoryTestProject/UnitTest1.cs | 13 +++++ .../EmployeeRepositoryUnitTests.cs | 2 + .../Lab.DAL/DefaultDbContextManager.cs | 58 +++++++++++-------- .../Lab.DAL/EmployeeRepository.cs | 8 +-- 5 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj new file mode 100644 index 00000000..5fb667f3 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj @@ -0,0 +1,16 @@ + + + + net5.0 + + false + + + + + + + + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs new file mode 100644 index 00000000..69366529 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs @@ -0,0 +1,13 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.DAL.InMemoryTestProject +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index b079931d..f30e2198 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -65,6 +65,8 @@ public void 操作真實資料庫_手動取得Repository執行個體() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetPhysicalDatabase(); + var repository = new EmployeeRepository(); var id = Guid.NewGuid(); repository.NewAsync(new NewRequest diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index a2bc9b3e..4a1c16d2 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -14,9 +14,11 @@ internal class DefaultDbContextManager private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; private static readonly ILoggerFactory s_loggerFactory; - private static ServiceProvider s_serviceProvider; - private static IConfiguration s_configuration; - private static DateTime? s_now; + + private static readonly ServiceCollection s_services; + private static ServiceProvider s_serviceProvider; + private static IConfiguration s_configuration; + private static DateTime? s_now; public static DateTime Now { @@ -60,16 +62,18 @@ public static IConfiguration Configuration set => s_configuration = value; } - private static ServiceCollection s_services; static DefaultDbContextManager() { s_services = new ServiceCollection(); - + s_serviceProviderLazy = new Lazy(() => { var services = s_services; services.AddDbContextFactory(ApplyConfigurePhysical); + + //services.AddDbContextFactory(ApplyConfigurePhysical); + // services.AddDbContextFactory(ApplyConfigureMemory); return services.BuildServiceProvider(); }); s_configurationLazy @@ -92,10 +96,33 @@ static DefaultDbContextManager() }); } + public static T GetInstance() + { + return ServiceProvider.GetService(); + } + + public static void SetMemoryDatabase() where TContext : DbContext + { + var services = s_services; + + services.Clear(); + services.AddDbContextFactory(ApplyConfigureMemory); + ServiceProvider = services.BuildServiceProvider(); + } + + public static void SetPhysicalDatabase() where TContext : DbContext + { + var services = s_services; + + services.Clear(); + services.AddDbContextFactory(ApplyConfigurePhysical); + ServiceProvider = services.BuildServiceProvider(); + } + private static void ApplyConfigureMemory(IServiceProvider provider, DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseInMemoryDatabase("Demo") + optionsBuilder.UseInMemoryDatabase("Lab.DAL") .UseLoggerFactory(s_loggerFactory) ; } @@ -114,24 +141,5 @@ private static void ApplyConfigurePhysical(IServiceProvider provider, .UseLoggerFactory(s_loggerFactory) ; } - - public static T GetInstance() - { - return ServiceProvider.GetService(); - } - - public static void SetPhysicalDatabase() where TContext : DbContext - { - var services = s_services; - services.AddDbContextFactory(ApplyConfigurePhysical); - ServiceProvider = services.BuildServiceProvider(); - } - - public static void SetMemoryDatabase() where TContext : DbContext - { - var services = s_services; - services.AddDbContextFactory(ApplyConfigureMemory); - ServiceProvider = services.BuildServiceProvider(); - } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index e0fef74c..09d00827 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -15,7 +15,7 @@ internal IDbContextFactory DbContextFactory { if (this._dbContextFactory == null) { - this._dbContextFactory = DefaultDbContextManager.GetInstance>(); + return DefaultDbContextManager.GetInstance>(); } return this._dbContextFactory; @@ -38,7 +38,7 @@ internal DateTime Now } private IDbContextFactory _dbContextFactory; - private DateTime? _now; + private DateTime? _now; public async Task InsertLogAsync(InsertOrderRequest request, string accessId, @@ -76,7 +76,7 @@ public async Task NewAsync(NewRequest request, CreateAt = this.Now, CreateBy = accessId }; - + var identityToDb = new Identity { Account = request.Account, @@ -86,7 +86,7 @@ public async Task NewAsync(NewRequest request, CreateAt = this.Now, CreateBy = accessId }; - + employeeToDb.Identity = identityToDb; await dbContext.Employees.AddAsync(employeeToDb, cancel); return await dbContext.SaveChangesAsync(cancel); From eb494beaff5445b7fa8d739e42f866c2d1f9f597 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 15:45:08 +0800 Subject: [PATCH 068/267] refactor --- .../Lab.DAL.InMemoryTestProject.csproj | 16 ---------------- .../Lab.DAL.InMemoryTestProject/UnitTest1.cs | 13 ------------- 2 files changed, 29 deletions(-) delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj delete mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj deleted file mode 100644 index 5fb667f3..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/Lab.DAL.InMemoryTestProject.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net5.0 - - false - - - - - - - - - - diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs deleted file mode 100644 index 69366529..00000000 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.InMemoryTestProject/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Lab.DAL.InMemoryTestProject -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestMethod1() - { - } - } -} \ No newline at end of file From e8d9477e8463ffc3d6534fcf416c0f011b668ea2 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 15:56:33 +0800 Subject: [PATCH 069/267] re --- .../EmployeeRepositoryUnitTests.cs | 56 +++++++++++++++++++ .../Lab.DAL/EmployeeRepository.cs | 19 ++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index f30e2198..264634b8 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -92,6 +92,62 @@ public void 操作真實資料庫_手動取得Repository執行個體() Assert.AreEqual(1, count); } + [TestMethod] + public void 操作真實資料庫_從容器取得Repository和EmployeeDbContext執行個體() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + + var connectionString = + "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.Injection;Trusted_Connection=True;MultipleActiveResultSets=true"; + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddSingleton(); + services.AddDbContext(builder => + { + builder.UseSqlServer(connectionString); + }); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + var repository = host.Services.GetService(); + var employeeDbContext = host.Services.GetService(); + employeeDbContext.Database.EnsureCreated(); + + repository.EmployeeDbContext = employeeDbContext; + + var id = Guid.NewGuid(); + + //act + var count = repository.NewAsync(new NewRequest + { + Id = id, + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + }, "TestUser").Result; + + //assert + Assert.AreEqual(2, count); + var db = new EmployeeDbContext(dbContextOptions); + + // var actual = db.Employees.FirstOrDefault(); + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + db.Database.EnsureDeleted(); + } + [TestMethod] public void 操作真實資料庫_從容器取得Repository執行個體() { diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index 09d00827..d480ba0b 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -23,6 +23,20 @@ internal IDbContextFactory DbContextFactory set => this._dbContextFactory = value; } + internal EmployeeDbContext EmployeeDbContext + { + get + { + if (this._employeeDbContext == null) + { + return this.DbContextFactory.CreateDbContext(); + } + + return this._employeeDbContext; + } + set => this._employeeDbContext = value; + } + internal DateTime Now { get @@ -38,13 +52,14 @@ internal DateTime Now } private IDbContextFactory _dbContextFactory; + private EmployeeDbContext _employeeDbContext; private DateTime? _now; public async Task InsertLogAsync(InsertOrderRequest request, string accessId, CancellationToken cancel = default) { - await using var dbContext = this.DbContextFactory.CreateDbContext(); + await using var dbContext = this.EmployeeDbContext; var toDbOrderHistory = new OrderHistory { @@ -64,7 +79,7 @@ public async Task NewAsync(NewRequest request, string accessId, CancellationToken cancel = default) { - await using var dbContext = this.DbContextFactory.CreateDbContext(); + await using var dbContext = this.EmployeeDbContext; var id = Guid.NewGuid(); var employeeToDb = new Employee From 8aafabc38283e4163a631185a25740fbe8f7e50e Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 17:12:52 +0800 Subject: [PATCH 070/267] refactor --- .../Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index d480ba0b..3a063e94 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -7,7 +7,18 @@ namespace Lab.DAL { - public class EmployeeRepository + public interface IEmployeeRepository + { + Task InsertLogAsync(InsertOrderRequest request, + string accessId, + CancellationToken cancel = default); + + Task NewAsync(NewRequest request, + string accessId, + CancellationToken cancel = default); + } + + public class EmployeeRepository : IEmployeeRepository { internal IDbContextFactory DbContextFactory { From cd9d27fffc7a88a435a5936f0add249b4b3c72ed Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 21:18:44 +0800 Subject: [PATCH 071/267] refactor --- .../Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs | 1 - .../Lab.DAL/EntityModel/EmployeeDbContext.cs | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 4a1c16d2..94015507 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -10,7 +10,6 @@ namespace Lab.DAL { internal class DefaultDbContextManager { - public static readonly bool[] Migrated = {false}; private static readonly Lazy s_serviceProviderLazy; private static readonly Lazy s_configurationLazy; private static readonly ILoggerFactory s_loggerFactory; diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs index 32c415b6..1e2b3860 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -11,17 +11,19 @@ public class EmployeeDbContext : DbContext public virtual DbSet OrderHistories { get; set; } + private static bool[] s_migrated; + public EmployeeDbContext(DbContextOptions options) : base(options) { - if (DefaultDbContextManager.Migrated[0]) + if (s_migrated[0]) { return; } - lock (DefaultDbContextManager.Migrated) + lock (s_migrated) { - if (DefaultDbContextManager.Migrated[0] == false) + if (s_migrated[0] == false) { var memoryOptions = options.FindExtension(); if (memoryOptions == null) @@ -29,7 +31,7 @@ public EmployeeDbContext(DbContextOptions options) this.Database.Migrate(); } - DefaultDbContextManager.Migrated[0] = true; + s_migrated[0] = true; } } } From 1ef21fc280957bb4bb6b202bf4a70f348aa2d585 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 21:22:38 +0800 Subject: [PATCH 072/267] fix bug --- .../Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs index 1e2b3860..3a04203a 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -5,14 +5,14 @@ namespace Lab.DAL.EntityModel { public class EmployeeDbContext : DbContext { + private static readonly bool[] s_migrated = {false}; + public virtual DbSet Employees { get; set; } public virtual DbSet Identities { get; set; } public virtual DbSet OrderHistories { get; set; } - private static bool[] s_migrated; - public EmployeeDbContext(DbContextOptions options) : base(options) { From 19ef31f4cd243f6073274c6aeda3b08ab709867f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Apr 2021 23:55:44 +0800 Subject: [PATCH 073/267] first add --- .../Lab.SQLite.TestProject.csproj | 24 +++ ORM/EFCore/Lab.SQLite/Lab.SQLite.sln | 22 +++ .../Lab.SQLite/DefaultDbContextManager.cs | 144 ++++++++++++++++++ .../Employee/InsertOrderRequest.cs | 15 ++ .../DomainModel/Employee/NewRequest.cs | 24 +++ .../Lab.SQLite/EmployeeContextFactory.cs | 27 ++++ .../Lab.SQLite/EmployeeRepository.cs | 121 +++++++++++++++ .../Lab.SQLite/EntityModel/Employee.cs | 32 ++++ .../EntityModel/EmployeeDbContext.cs | 81 ++++++++++ .../Lab.SQLite/EntityModel/Identity.cs | 34 +++++ .../Lab.SQLite/EntityModel/OrderHistory.cs | 31 ++++ .../Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj | 23 +++ .../Lab.SQLite/Lab.SQLite/appsettings.json | 5 + 13 files changed, 583 insertions(+) create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.sln create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj new file mode 100644 index 00000000..01eb2648 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj @@ -0,0 +1,24 @@ + + + + net5.0 + bin + false + + + + + + + + + + + + + + + + + + diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln b/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln new file mode 100644 index 00000000..1bb14120 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SQLite", "Lab.SQLite\Lab.SQLite.csproj", "{ECC70BD1-FB1E-442C-B676-386C11510B43}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SQLite.TestProject", "Lab.SQLite.TestProject\Lab.SQLite.TestProject.csproj", "{C3821F71-3F5F-4480-8D84-782A55772101}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECC70BD1-FB1E-442C-B676-386C11510B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECC70BD1-FB1E-442C-B676-386C11510B43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECC70BD1-FB1E-442C-B676-386C11510B43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECC70BD1-FB1E-442C-B676-386C11510B43}.Release|Any CPU.Build.0 = Release|Any CPU + {C3821F71-3F5F-4480-8D84-782A55772101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3821F71-3F5F-4480-8D84-782A55772101}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3821F71-3F5F-4480-8D84-782A55772101}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3821F71-3F5F-4480-8D84-782A55772101}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs new file mode 100644 index 00000000..08bdb7c2 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using Lab.SQLite.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.SQLite +{ + internal class DefaultDbContextManager + { + private static readonly Lazy s_serviceProviderLazy; + private static readonly Lazy s_configurationLazy; + private static readonly ILoggerFactory s_loggerFactory; + + private static readonly ServiceCollection s_services; + private static ServiceProvider s_serviceProvider; + private static IConfiguration s_configuration; + private static DateTime? s_now; + + public static DateTime Now + { + get + { + if (s_now == null) + { + return DateTime.UtcNow; + } + + return s_now.Value; + } + set => s_now = value; + } + + public static ServiceProvider ServiceProvider + { + get + { + if (s_serviceProvider == null) + { + s_serviceProvider = s_serviceProviderLazy.Value; + } + + return s_serviceProvider; + } + set => s_serviceProvider = value; + } + + public static IConfiguration Configuration + { + get + { + if (s_configuration == null) + { + s_configuration = s_configurationLazy.Value; + } + + return s_configuration; + } + set => s_configuration = value; + } + + static DefaultDbContextManager() + { + s_services = new ServiceCollection(); + + s_serviceProviderLazy = + new Lazy(() => + { + var services = s_services; + services.AddDbContextFactory(ApplyConfigurePhysical); + + //services.AddDbContextFactory(ApplyConfigurePhysical); + // services.AddDbContextFactory(ApplyConfigureMemory); + return services.BuildServiceProvider(); + }); + s_configurationLazy + = new Lazy(() => + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + return configBuilder.Build(); + }); + s_loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + } + + public static T GetInstance() + { + return ServiceProvider.GetService(); + } + + public static void SetMemoryDatabase() where TContext : DbContext + { + var services = s_services; + + services.Clear(); + services.AddDbContextFactory(ApplyConfigureMemory); + ServiceProvider = services.BuildServiceProvider(); + } + + public static void SetPhysicalDatabase() where TContext : DbContext + { + var services = s_services; + + services.Clear(); + services.AddDbContextFactory(ApplyConfigurePhysical); + ServiceProvider = services.BuildServiceProvider(); + } + + private static void ApplyConfigureMemory(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseInMemoryDatabase("Lab.DAL") + .UseLoggerFactory(s_loggerFactory) + ; + } + + private static void ApplyConfigurePhysical(IServiceProvider provider, + DbContextOptionsBuilder optionsBuilder) + { + var config = provider.GetService(); + if (config == null) + { + config = Configuration; + } + + var connectionString = config.GetConnectionString("DefaultConnection"); + optionsBuilder.UseSqlite(connectionString) + .UseLoggerFactory(s_loggerFactory) + ; + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs new file mode 100644 index 00000000..bb552170 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs @@ -0,0 +1,15 @@ +using System; + +namespace Lab.SQLite.DomainModel.Employee +{ + public class InsertOrderRequest + { + public Guid? Employee_Id { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs new file mode 100644 index 00000000..7719a278 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Lab.SQLite.DomainModel.Employee +{ + public class NewRequest + { + [Key] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs new file mode 100644 index 00000000..1a3a45c4 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs @@ -0,0 +1,27 @@ +using System; +using Lab.SQLite.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace Lab.SQLite +{ + public class EmployeeContextFactory : IDesignTimeDbContextFactory + { + public EmployeeDbContext CreateDbContext(string[] args) + { + Console.WriteLine("由設計工具產生 Database,初始化 DbContextOptionsBuilder"); + + var config = DefaultDbContextManager.Configuration; + var connectionString = config.GetConnectionString("DefaultConnection"); + + Console.WriteLine($"由 appsettings.json 讀取連線字串為:{connectionString}"); + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlite(connectionString); + Console.WriteLine($"DbContextOptionsBuilder 設定完成"); + + return new EmployeeDbContext(optionsBuilder.Options); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs new file mode 100644 index 00000000..1e932f4e --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Lab.SQLite.DomainModel.Employee; +using Lab.SQLite.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.SQLite +{ + public interface IEmployeeRepository + { + Task InsertLogAsync(InsertOrderRequest request, + string accessId, + CancellationToken cancel = default); + + Task NewAsync(NewRequest request, + string accessId, + CancellationToken cancel = default); + } + + public class EmployeeRepository : IEmployeeRepository + { + internal IDbContextFactory DbContextFactory + { + get + { + if (this._dbContextFactory == null) + { + return DefaultDbContextManager.GetInstance>(); + } + + return this._dbContextFactory; + } + set => this._dbContextFactory = value; + } + + internal EmployeeDbContext EmployeeDbContext + { + get + { + if (this._employeeDbContext == null) + { + return this.DbContextFactory.CreateDbContext(); + } + + return this._employeeDbContext; + } + set => this._employeeDbContext = value; + } + + internal DateTime Now + { + get + { + if (this._now == null) + { + return DefaultDbContextManager.Now; + } + + return this._now.Value; + } + set => this._now = value; + } + + private IDbContextFactory _dbContextFactory; + private EmployeeDbContext _employeeDbContext; + private DateTime? _now; + + public async Task InsertLogAsync(InsertOrderRequest request, + string accessId, + CancellationToken cancel = default) + { + await using var dbContext = this.EmployeeDbContext; + + var toDbOrderHistory = new OrderHistory + { + Employee_Id = request.Employee_Id, + Product_Id = request.Product_Id, + Product_Name = request.Product_Id, + CreateAt = this.Now, + CreateBy = accessId, + Remark = request.Remark, + }; + + await dbContext.OrderHistories.AddAsync(toDbOrderHistory, cancel); + return await dbContext.SaveChangesAsync(cancel); + } + + public async Task NewAsync(NewRequest request, + string accessId, + CancellationToken cancel = default) + { + await using var dbContext = this.EmployeeDbContext; + + var id = Guid.NewGuid(); + var employeeToDb = new Employee + { + Id = id, + Name = request.Name, + Age = request.Age, + Remark = request.Remark, + CreateAt = this.Now, + CreateBy = accessId + }; + + var identityToDb = new Identity + { + Account = request.Account, + Password = request.Password, + Remark = request.Remark, + Employee = employeeToDb, + CreateAt = this.Now, + CreateBy = accessId + }; + + employeeToDb.Identity = identityToDb; + await dbContext.Employees.AddAsync(employeeToDb, cancel); + return await dbContext.SaveChangesAsync(cancel); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs new file mode 100644 index 00000000..5f9596f1 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.SQLite.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..30d07caa --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; + +namespace Lab.SQLite.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = {false}; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + if (memoryOptions == null) + { + this.Database.Migrate(); + } + + s_migrated[0] = true; + } + } + } + + // 給 Migration CLI 使用 + // 建構函數配置失敗才需要以下處理 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // var connectionString = + // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; + // + // // var connectionString = this._connectionString; + // if (optionsBuilder.IsConfigured == false) + // { + // optionsBuilder.UseSqlServer(connectionString); + // } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // modelBuilder.Entity(p => + // { + // p.HasKey(e => e.Id) + // .IsClustered(false); + // }); + // + // modelBuilder.Entity(p => + // { + // p.HasIndex(e => e.SequenceId) + // .IsUnique() + // .IsClustered(); + // }); + // modelBuilder.Entity(p => + // { + // p.HasKey(e => e.Employee_Id) + // .IsClustered(false); + // }); + // modelBuilder.Entity(p => + // { + // p.HasIndex(e => e.SequenceId) + // .IsUnique() + // .IsClustered(); + // }); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs new file mode 100644 index 00000000..1f7947c1 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.SQLite.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..12404287 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.SQLite.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj b/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj new file mode 100644 index 00000000..afd9f79a --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj @@ -0,0 +1,23 @@ + + + + net5.0 + + + + + + + + + + + + + + + + <_Parameter1>Lab.SQLite.TestProject + + + diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json new file mode 100644 index 00000000..be206a85 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=bin\\Lab.DAL.db" + } +} \ No newline at end of file From 35f7904b55ac484d9c6d255be98618bce2b3db9b Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 11:21:15 +0800 Subject: [PATCH 074/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 19 ++++++++----------- .../DomainModel/Employee/NewRequest.cs | 3 --- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 264634b8..206064d0 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -68,16 +68,18 @@ public void 操作真實資料庫_手動取得Repository執行個體() DefaultDbContextManager.SetPhysicalDatabase(); var repository = new EmployeeRepository(); - var id = Guid.NewGuid(); + + // var id = Guid.NewGuid(); repository.NewAsync(new NewRequest { - Id = id, Account = "yao", Password = "123456", Name = "余小章", Age = 18, Remark = "測試案例,持續航向偉大航道" }, "TestUser").Wait(); + using var db = new EmployeeDbContext(s_employeeContextOptions); + var id = db.Employees.FirstOrDefault(p => p.Name == "余小章").Id; //act var count = repository.InsertLogAsync(new InsertOrderRequest @@ -93,7 +95,7 @@ public void 操作真實資料庫_手動取得Repository執行個體() } [TestMethod] - public void 操作真實資料庫_從容器取得Repository和EmployeeDbContext執行個體() + public void 操作真實資料庫_注入EmployeeDbContext() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); @@ -124,7 +126,6 @@ public void 操作真實資料庫_從容器取得Repository和EmployeeDbContext //act var count = repository.NewAsync(new NewRequest { - Id = id, Account = "yao", Password = "123456", Name = "余小章", @@ -133,9 +134,8 @@ public void 操作真實資料庫_從容器取得Repository和EmployeeDbContext //assert Assert.AreEqual(2, count); - var db = new EmployeeDbContext(dbContextOptions); + using var db = new EmployeeDbContext(dbContextOptions); - // var actual = db.Employees.FirstOrDefault(); var actual = db.Employees .Include(p => p.Identity) .AsNoTracking() @@ -149,7 +149,7 @@ public void 操作真實資料庫_從容器取得Repository和EmployeeDbContext } [TestMethod] - public void 操作真實資料庫_從容器取得Repository執行個體() + public void 操作真實資料庫_預設EmployeeDbContext() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); @@ -160,12 +160,10 @@ public void 操作真實資料庫_從容器取得Repository執行個體() var host = builder.Build(); var repository = host.Services.GetService(); - var id = Guid.NewGuid(); //act var count = repository.NewAsync(new NewRequest { - Id = id, Account = "yao", Password = "123456", Name = "余小章", @@ -174,9 +172,8 @@ public void 操作真實資料庫_從容器取得Repository執行個體() //assert Assert.AreEqual(2, count); - var db = new EmployeeDbContext(s_employeeContextOptions); + using var db = new EmployeeDbContext(s_employeeContextOptions); - // var actual = db.Employees.FirstOrDefault(); var actual = db.Employees .Include(p => p.Identity) .AsNoTracking() diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs index 3288a612..ed8418dc 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/NewRequest.cs @@ -5,9 +5,6 @@ namespace Lab.DAL.DomainModel.Employee { public class NewRequest { - [Key] - public Guid Id { get; set; } - [Required] public string Name { get; set; } From 3be7ed326fb4873254997ed464df630d4ee29d96 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 12:00:27 +0800 Subject: [PATCH 075/267] refactor --- .../Lab.DAL.TestProject/EmployeeDbContext.cs | 61 +++++++++++++++++++ .../EmployeeRepositoryUnitTests.cs | 41 +++++++++---- .../Lab.DAL/EmployeeRepository.cs | 2 +- 3 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs new file mode 100644 index 00000000..1888c309 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs @@ -0,0 +1,61 @@ +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; + +namespace Lab.DAL.TestProject +{ + public class TestEmployeeDbContext : DbContext + { + private readonly string _connectionString; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public TestEmployeeDbContext(string connectionString) + { + this._connectionString = connectionString; + } + + // 給 Migration CLI 使用 + // 建構函數配置失敗才需要以下處理 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var connectionString = this._connectionString; + if (optionsBuilder.IsConfigured == false) + { + optionsBuilder.UseSqlServer(connectionString); + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + }); + + modelBuilder.Entity(p => + { + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + }); + modelBuilder.Entity(p => + { + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 206064d0..860a0a25 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -10,16 +10,24 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Lab.DAL.UnitTest +namespace Lab.DAL.TestProject { [TestClass] public class EmployeeRepositoryUnitTests { private static readonly DbContextOptions s_employeeContextOptions; + public static string TestDbConnectionString; static EmployeeRepositoryUnitTests() { s_employeeContextOptions = CreateDbContextOptions(); + + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + + var configRoot = configBuilder.Build(); + TestDbConnectionString = configRoot.GetConnectionString("DefaultConnection"); } [AssemblyCleanup] @@ -28,7 +36,7 @@ public static void AssemblyCleanup() //刪除測試資料庫 Console.WriteLine("AssemblyCleanup"); - using var db = new EmployeeDbContext(s_employeeContextOptions); + using var db = new TestEmployeeDbContext(TestDbConnectionString); db.Database.EnsureDeleted(); } @@ -37,11 +45,8 @@ public static void AssemblyInitialize(TestContext context) { //刪除測試資料庫 Console.WriteLine("AssemblyInitialize"); - using var db = new EmployeeDbContext(s_employeeContextOptions); + using var db = new TestEmployeeDbContext(TestDbConnectionString); db.Database.EnsureDeleted(); - - //建立測試資料庫 - db.Database.EnsureCreated(); } [ClassCleanup] @@ -92,6 +97,15 @@ public void 操作真實資料庫_手動取得Repository執行個體() //assert Assert.AreEqual(1, count); + + using var db1 = new TestEmployeeDbContext(TestDbConnectionString); + + var actual = db1.OrderHistories + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual("A001", actual.Product_Id); + Assert.AreEqual("羅技滑鼠", actual.Product_Name); } [TestMethod] @@ -121,8 +135,6 @@ public void 操作真實資料庫_注入EmployeeDbContext() repository.EmployeeDbContext = employeeDbContext; - var id = Guid.NewGuid(); - //act var count = repository.NewAsync(new NewRequest { @@ -172,7 +184,8 @@ public void 操作真實資料庫_預設EmployeeDbContext() //assert Assert.AreEqual(2, count); - using var db = new EmployeeDbContext(s_employeeContextOptions); + + using var db = new TestEmployeeDbContext(TestDbConnectionString); var actual = db.Employees .Include(p => p.Identity) @@ -227,9 +240,13 @@ private static DbContextOptions CreateDbContextOptions() private static void DeleteTestDataRow() { - var dbContextOptions = s_employeeContextOptions; - using var db = new EmployeeDbContext(dbContextOptions); - var deleteCommand = GetDeleteAllRecordCommand(); + using var db = new TestEmployeeDbContext(TestDbConnectionString); + if (db.Database.CanConnect() == false) + { + return; + } + + var deleteCommand = GetDeleteAllRecordCommand(); db.Database.ExecuteSqlRaw(deleteCommand); } diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index 3a063e94..f6703c41 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -76,7 +76,7 @@ public async Task InsertLogAsync(InsertOrderRequest request, { Employee_Id = request.Employee_Id, Product_Id = request.Product_Id, - Product_Name = request.Product_Id, + Product_Name = request.Product_Name, CreateAt = this.Now, CreateBy = accessId, Remark = request.Remark, From 4ed7925158b7a4829fc48a388339bc732b3738f3 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 12:01:38 +0800 Subject: [PATCH 076/267] refactor --- .../{EmployeeDbContext.cs => TestEmployeeDbContext.cs} | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) rename ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/{EmployeeDbContext.cs => TestEmployeeDbContext.cs} (90%) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/TestEmployeeDbContext.cs similarity index 90% rename from ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs rename to ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/TestEmployeeDbContext.cs index 1888c309..202ad138 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/TestEmployeeDbContext.cs @@ -1,26 +1,23 @@ using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; namespace Lab.DAL.TestProject { public class TestEmployeeDbContext : DbContext { - private readonly string _connectionString; - public virtual DbSet Employees { get; set; } public virtual DbSet Identities { get; set; } public virtual DbSet OrderHistories { get; set; } + private readonly string _connectionString; + public TestEmployeeDbContext(string connectionString) { this._connectionString = connectionString; } - // 給 Migration CLI 使用 - // 建構函數配置失敗才需要以下處理 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { var connectionString = this._connectionString; From 168afe7dc467fd65e340ba1d21c4e2aa7e3f66cd Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 13:05:57 +0800 Subject: [PATCH 077/267] refactor --- ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs index 94015507..999d62af 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DefaultDbContextManager.cs @@ -70,9 +70,6 @@ static DefaultDbContextManager() { var services = s_services; services.AddDbContextFactory(ApplyConfigurePhysical); - - //services.AddDbContextFactory(ApplyConfigurePhysical); - // services.AddDbContextFactory(ApplyConfigureMemory); return services.BuildServiceProvider(); }); s_configurationLazy From 51632f9e5eb7a261bf82b41f13c817309a40061e Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 14:43:19 +0800 Subject: [PATCH 078/267] refactor --- .../Lab.DAL/EntityModel/EmployeeDbContext.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs index 3a04203a..78b0f738 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -1,5 +1,7 @@ -using Microsoft.EntityFrameworkCore; +using System; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; namespace Lab.DAL.EntityModel { @@ -26,9 +28,15 @@ public EmployeeDbContext(DbContextOptions options) if (s_migrated[0] == false) { var memoryOptions = options.FindExtension(); + if (memoryOptions == null) { - this.Database.Migrate(); + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine($"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } } s_migrated[0] = true; From ab8fbbb347cb80c70501145b6f13df8ccc5d0096 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 15:44:56 +0800 Subject: [PATCH 079/267] add web api project --- .../DomainModel/Employee/FilterResponse.cs | 15 +++++ .../Lab.DAL/EmployeeRepository.cs | 19 +++++++ ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln | 6 ++ .../Controllers/DefaultController.cs | 57 +++++++++++++++++++ .../Lab.WebApi/Lab.WebApi.csproj | 15 +++++ .../Lab.VirtualDb/Lab.WebApi/Program.cs | 23 ++++++++ .../Lab.WebApi/Properties/launchSettings.json | 31 ++++++++++ .../ServiceModel/Employee/FilterResponse.cs | 16 ++++++ .../ServiceModel/Employee/NewRequest.cs | 20 +++++++ .../Lab.VirtualDb/Lab.WebApi/Startup.cs | 57 +++++++++++++++++++ .../Lab.WebApi/WeatherForecast.cs | 15 +++++ .../Lab.WebApi/appsettings.Development.json | 9 +++ .../Lab.VirtualDb/Lab.WebApi/appsettings.json | 13 +++++ 13 files changed, 296 insertions(+) create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/FilterResponse.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Controllers/DefaultController.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Lab.WebApi.csproj create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Program.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Properties/launchSettings.json create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/FilterResponse.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/NewRequest.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Startup.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/WeatherForecast.cs create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.Development.json create mode 100644 ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.json diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/FilterResponse.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/FilterResponse.cs new file mode 100644 index 00000000..8aa958a4 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/DomainModel/Employee/FilterResponse.cs @@ -0,0 +1,15 @@ +namespace Lab.DAL.DomainModel.Employee +{ + public class FilterResponse + { + public string Name { get; set; } + + public int? Age { get; set; } + + public string Account { get; set; } + + public string Password { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs index f6703c41..68c05d52 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/EmployeeRepository.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Lab.DAL.DomainModel.Employee; @@ -117,5 +119,22 @@ public async Task NewAsync(NewRequest request, await dbContext.Employees.AddAsync(employeeToDb, cancel); return await dbContext.SaveChangesAsync(cancel); } + + public async Task> GetAllAsync(CancellationToken cancel) + { + await using var db = this.EmployeeDbContext; + return await db.Employees + .Include(p => p.Identity) + .Select(p => new FilterResponse() + { + Account = p.Identity.Account, + Age = p.Age, + Name = p.Name, + Password = p.Identity.Password, + Remark = p.Remark + }) + .AsNoTracking() + .ToListAsync(cancel); + } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln index f401e0a4..fa88dca5 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln +++ b/ORM/EFCore/Lab.VirtualDb/Lab.VirtualDb.sln @@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL.TestProject", "Lab. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Biz", "Lab.Biz\Lab.Biz.csproj", "{0056BEF7-8B47-4387-9110-788A3B73E452}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.WebApi", "Lab.WebApi\Lab.WebApi.csproj", "{7C5880FB-4D25-4188-A04C-CE58C6D592A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +26,9 @@ Global {0056BEF7-8B47-4387-9110-788A3B73E452}.Debug|Any CPU.Build.0 = Debug|Any CPU {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.ActiveCfg = Release|Any CPU {0056BEF7-8B47-4387-9110-788A3B73E452}.Release|Any CPU.Build.0 = Release|Any CPU + {7C5880FB-4D25-4188-A04C-CE58C6D592A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C5880FB-4D25-4188-A04C-CE58C6D592A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C5880FB-4D25-4188-A04C-CE58C6D592A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C5880FB-4D25-4188-A04C-CE58C6D592A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Controllers/DefaultController.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Controllers/DefaultController.cs new file mode 100644 index 00000000..385cb904 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Controllers/DefaultController.cs @@ -0,0 +1,57 @@ +using System.Threading; +using System.Threading.Tasks; +using Lab.DAL; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using ServiceModel = Lab.WebApi.ServiceModel; +using DomainModel = Lab.DAL.DomainModel; + +namespace Lab.WebApi.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private readonly ILogger _logger; + private readonly EmployeeRepository _repository; + + public DefaultController(ILogger logger, EmployeeRepository repository) + { + this._logger = logger; + this._repository = repository; + } + + [HttpGet] + [Produces(typeof(ServiceModel.Employee.FilterResponse))] + public async Task Get(CancellationToken cancel = default) + { + var repository = this._repository; + var record = await repository.GetAllAsync(cancel); + return this.Ok(record); + } + + [HttpPost] + public async Task Post(ServiceModel.Employee.NewRequest request, + string accessId, + CancellationToken cancel = default) + { + var repository = this._repository; + var count = await repository.NewAsync(new DomainModel.Employee.NewRequest() + { + Account = request.Account, + Age = request.Age, + Name = request.Name, + Password = request.Password, + Remark = request.Remark + }, accessId, cancel); + if (count == 2) + { + return this.Ok(); + } + else + { + return this.NoContent(); + } + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Lab.WebApi.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Lab.WebApi.csproj new file mode 100644 index 00000000..8b969acc --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Lab.WebApi.csproj @@ -0,0 +1,15 @@ + + + + net5.0 + + + + + + + + + + + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Program.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Program.cs new file mode 100644 index 00000000..3fd195ba --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Lab.WebApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Properties/launchSettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Properties/launchSettings.json new file mode 100644 index 00000000..292d93d5 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:24269", + "sslPort": 44335 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Lab.WebApi": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/FilterResponse.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/FilterResponse.cs new file mode 100644 index 00000000..257ab643 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/FilterResponse.cs @@ -0,0 +1,16 @@ +namespace Lab.WebApi.ServiceModel.Employee +{ + //for doc + public class FilterResponse + { + public string Name { get; set; } + + public int? Age { get; set; } + + public string Account { get; set; } + + public string Password { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/NewRequest.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/NewRequest.cs new file mode 100644 index 00000000..e26362fb --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/ServiceModel/Employee/NewRequest.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.WebApi.ServiceModel.Employee +{ + public class NewRequest + { + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Startup.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Startup.cs new file mode 100644 index 00000000..4430ede3 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/Startup.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Lab.DAL; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; + +namespace Lab.WebApi +{ + 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.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo {Title = "Lab.WebApi", Version = "v1"}); + }); + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Lab.WebApi v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/WeatherForecast.cs b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/WeatherForecast.cs new file mode 100644 index 00000000..1c8dfc31 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace Lab.WebApi +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.Development.json b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.json b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.json new file mode 100644 index 00000000..64195df2 --- /dev/null +++ b/ORM/EFCore/Lab.VirtualDb/Lab.WebApi/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.WebApi;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} From 97a892c7c757589e44a31a7f0b827dfe9cdf5e73 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 15:45:26 +0800 Subject: [PATCH 080/267] add sqlite project --- .../EmployeeRepositoryUnitTests.cs | 253 ++++++++++++++++++ .../Lab.SQLite.TestProject.csproj | 19 +- .../TestEmployeeDbContext.cs | 39 +++ .../Lab.SQLite.TestProject/appsettings.json | 5 + .../Lab.SQLite/DefaultDbContextManager.cs | 3 - .../Employee/InsertOrderRequest.cs | 2 +- .../DomainModel/Employee/NewRequest.cs | 3 - .../Lab.SQLite/EmployeeRepository.cs | 3 +- .../Lab.SQLite/EntityModel/Employee.cs | 5 +- .../EntityModel/EmployeeDbContext.cs | 41 ++- .../Lab.SQLite/EntityModel/Identity.cs | 7 +- .../Lab.SQLite/EntityModel/OrderHistory.cs | 11 +- .../Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj | 10 +- 13 files changed, 345 insertions(+), 56 deletions(-) create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs new file mode 100644 index 00000000..2bc478ee --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs @@ -0,0 +1,253 @@ +using System; +using System.IO; +using System.Linq; +using Lab.SQLite.DomainModel.Employee; +using Lab.SQLite.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.SQLite.TestProject +{ + [TestClass] + public class EmployeeRepositoryUnitTests + { + private static readonly DbContextOptions s_employeeContextOptions; + private static readonly string TestDbConnectionString = "Data Source=Lab.DAL.TestProject.db"; + + static EmployeeRepositoryUnitTests() + { + s_employeeContextOptions = CreateDbContextOptions(); + } + + [AssemblyCleanup] + public static void AssemblyCleanup() + { + //刪除測試資料庫 + Console.WriteLine("AssemblyCleanup"); + + using var db = new TestEmployeeDbContext(TestDbConnectionString); + db.Database.EnsureDeleted(); + } + + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + { + //刪除測試資料庫 + Console.WriteLine("AssemblyInitialize"); + using var db = new TestEmployeeDbContext(TestDbConnectionString); + db.Database.EnsureDeleted(); + + // //建立測試資料庫 + // db.Database.Migrate(); + } + + [ClassCleanup] + public static void ClassCleanup() + { + //刪除測試資料表 + Console.WriteLine("ClassCleanup"); + + // DeleteTestDataRow(); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + //刪除測試資料表 + Console.WriteLine("ClassInitialize"); + + // DeleteTestDataRow(); + } + + [TestMethod] + public void 操作真實資料庫_手動取得Repository執行個體() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetPhysicalDatabase(); + + var repository = new EmployeeRepository(); + + // var id = Guid.NewGuid(); + repository.NewAsync(new NewRequest + { + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Wait(); + using var db = new TestEmployeeDbContext(TestDbConnectionString); + var id = db.Employees.FirstOrDefault(p => p.Name == "余小章").Id; + + //act + var count = repository.InsertLogAsync(new InsertOrderRequest + { + Employee_Id = id, + Product_Id = "A001", + Product_Name = "羅技滑鼠", + Remark = "測試案例,持續航向偉大航道" + }, "TestUser").Result; + + //assert + Assert.AreEqual(1, count); + } + + [TestMethod] + public void 操作真實資料庫_注入EmployeeDbContext() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + + var connectionString = "Data Source=Lab.DAL.Injection.db"; + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddSingleton(); + services.AddDbContext(builder => + { + builder.UseSqlite(connectionString); + }); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + var repository = host.Services.GetService(); + var employeeDbContext = host.Services.GetService(); + // employeeDbContext.Database.Migrate(); + + repository.EmployeeDbContext = employeeDbContext; + + //act + var count = repository.NewAsync(new NewRequest + { + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + }, "TestUser").Result; + + //assert + Assert.AreEqual(2, count); + using var db = new EmployeeDbContext(dbContextOptions); + + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + db.Database.EnsureDeleted(); + } + + [TestMethod] + public void 操作真實資料庫_預設EmployeeDbContext() + { + //arrange + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetPhysicalDatabase(); + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + + var repository = host.Services.GetService(); + + //act + var count = repository.NewAsync(new NewRequest + { + Account = "yao", + Password = "123456", + Name = "余小章", + Age = 18, + }, "TestUser").Result; + + //assert + Assert.AreEqual(2, count); + using var db = new TestEmployeeDbContext(TestDbConnectionString); + + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + } + + [TestMethod] + public void 操作記憶體() + { + DefaultDbContextManager.Now = new DateTime(1900, 1, 1); + DefaultDbContextManager.SetMemoryDatabase(); + + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => { services.AddSingleton(); }); + var host = builder.Build(); + + var repository = host.Services.GetService(); + var count = repository.NewAsync(new NewRequest(), "TestUser").Result; + Assert.AreEqual(2, count); + } + + private static DbContextOptions CreateDbContextOptions() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + + var configRoot = configBuilder.Build(); + var connectionString = configRoot.GetConnectionString("DefaultConnection"); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + + //.AddFilter("Microsoft", LogLevel.Warning) + //.AddFilter("System", LogLevel.Warning) + .AddFilter("Lab.DAL", LogLevel.Debug) + .AddConsole() + ; + }); + return new DbContextOptionsBuilder() + .UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + .Options; + } + + private static void DeleteTestDataRow() + { + var dbContextOptions = s_employeeContextOptions; + using var db = new EmployeeDbContext(dbContextOptions); + var deleteCommand = GetDeleteAllRecordCommand(); + db.Database.ExecuteSqlRaw(deleteCommand); + } + + private static string GetDeleteAllRecordCommand() + { + var sql = @" +-- disable referential integrity +EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL' + + +EXEC sp_MSForEachTable 'DELETE FROM ?' + + +-- enable referential integrity again +EXEC sp_MSForEachTable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL' +"; + + return sql; + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj index 01eb2648..43a4d622 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj @@ -12,8 +12,9 @@ - + + @@ -21,4 +22,20 @@ + + + Always + + + + + + + + + + + + + diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs new file mode 100644 index 00000000..ffdf16eb --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Lab.SQLite.EntityModel +{ + public class TestEmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = {false}; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + private readonly string _connectionString; + + public TestEmployeeDbContext(string connectionString) + { + this._connectionString = connectionString; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var connectionString = this._connectionString; + if (optionsBuilder.IsConfigured == false) + { + Console.WriteLine($"設定連線字串:{connectionString}"); + optionsBuilder.UseSqlite(connectionString); + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json new file mode 100644 index 00000000..a83c326c --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=Lab.DAL.TestProject.db" + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs index 08bdb7c2..bc0a0261 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs @@ -70,9 +70,6 @@ static DefaultDbContextManager() { var services = s_services; services.AddDbContextFactory(ApplyConfigurePhysical); - - //services.AddDbContextFactory(ApplyConfigurePhysical); - // services.AddDbContextFactory(ApplyConfigureMemory); return services.BuildServiceProvider(); }); s_configurationLazy diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs index bb552170..0c53a98b 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs @@ -4,7 +4,7 @@ namespace Lab.SQLite.DomainModel.Employee { public class InsertOrderRequest { - public Guid? Employee_Id { get; set; } + public string Employee_Id { get; set; } public string Product_Id { get; set; } diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs index 7719a278..03987557 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs @@ -5,9 +5,6 @@ namespace Lab.SQLite.DomainModel.Employee { public class NewRequest { - [Key] - public Guid Id { get; set; } - [Required] public string Name { get; set; } diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs index 1e932f4e..a968face 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs @@ -74,6 +74,7 @@ public async Task InsertLogAsync(InsertOrderRequest request, var toDbOrderHistory = new OrderHistory { + Id = Guid.NewGuid().ToString(), Employee_Id = request.Employee_Id, Product_Id = request.Product_Id, Product_Name = request.Product_Id, @@ -92,7 +93,7 @@ public async Task NewAsync(NewRequest request, { await using var dbContext = this.EmployeeDbContext; - var id = Guid.NewGuid(); + var id = Guid.NewGuid().ToString(); var employeeToDb = new Employee { Id = id, diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs index 5f9596f1..371948bd 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs @@ -9,16 +9,13 @@ public class Employee { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Id { get; set; } + public string Id { get; set; } [Required] public string Name { get; set; } public int? Age { get; set; } - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } - public string Remark { get; set; } [Required] diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs index 30d07caa..842b7602 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs @@ -1,5 +1,8 @@ -using Microsoft.EntityFrameworkCore; +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; namespace Lab.SQLite.EntityModel { @@ -25,7 +28,14 @@ public EmployeeDbContext(DbContextOptions options) { if (s_migrated[0] == false) { - var memoryOptions = options.FindExtension(); + var memoryOptions = options.FindExtension(); + var sqliteOptionsExtension = options.FindExtension(); + + if (sqliteOptionsExtension != null) + { + Console.WriteLine($"EmployeeDbContext 的連線字串為:{sqliteOptionsExtension.ConnectionString},執行 Migration"); + } + if (memoryOptions == null) { this.Database.Migrate(); @@ -46,36 +56,15 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) // // var connectionString = this._connectionString; // if (optionsBuilder.IsConfigured == false) // { - // optionsBuilder.UseSqlServer(connectionString); + // Console.WriteLine("OnConfiguring"); + // optionsBuilder.UseSqlite(connectionString); // } } //管理索引 protected override void OnModelCreating(ModelBuilder modelBuilder) { - // modelBuilder.Entity(p => - // { - // p.HasKey(e => e.Id) - // .IsClustered(false); - // }); - // - // modelBuilder.Entity(p => - // { - // p.HasIndex(e => e.SequenceId) - // .IsUnique() - // .IsClustered(); - // }); - // modelBuilder.Entity(p => - // { - // p.HasKey(e => e.Employee_Id) - // .IsClustered(false); - // }); - // modelBuilder.Entity(p => - // { - // p.HasIndex(e => e.SequenceId) - // .IsUnique() - // .IsClustered(); - // }); + Console.WriteLine("設定資料表定義"); } } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs index 1f7947c1..e5c9949d 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs @@ -9,17 +9,14 @@ public class Identity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Employee_Id { get; set; } + public string Employee_Id { get; set; } [Required] public string Account { get; set; } [Required] public string Password { get; set; } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } - + public string Remark { get; set; } [Required] diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs index 12404287..9b015c57 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs @@ -8,16 +8,13 @@ namespace Lab.SQLite.EntityModel public class OrderHistory { [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid Id { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public string Id { get; set; } - public Guid? Employee_Id { get; set; } + public string Employee_Id { get; set; } public string Remark { get; set; } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } - + public string Product_Id { get; set; } public string Product_Name { get; set; } diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj b/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj index afd9f79a..71f18786 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj @@ -2,19 +2,19 @@ net5.0 - - + bin + bin\Lab.DAL.xml - + - + - + <_Parameter1>Lab.SQLite.TestProject From 59ad406276db2eef49186dd4cd0d1efa0a8b2922 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 17:54:11 +0800 Subject: [PATCH 081/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 6 +-- .../Lab.DAL.TestProject.csproj} | 39 +++++++------------ .../TestEmployeeDbContext.cs | 5 +-- .../Lab.DAL.TestProject/appsettings.json | 5 +++ .../DefaultDbContextManager.cs | 4 +- .../Employee/InsertOrderRequest.cs | 4 +- .../DomainModel/Employee/NewRequest.cs | 3 +- .../EmployeeContextFactory.cs | 10 ++--- .../EmployeeRepository.cs | 6 +-- .../EntityModel/Employee.cs | 2 +- .../EntityModel/EmployeeDbContext.cs | 18 ++++----- .../EntityModel/Identity.cs | 2 +- .../EntityModel/OrderHistory.cs | 2 +- .../Lab.DAL.csproj} | 8 +++- .../{Lab.SQLite => Lab.DAL}/appsettings.json | 0 .../Lab.SQLite.TestProject/appsettings.json | 5 --- ORM/EFCore/Lab.SQLite/Lab.SQLite.sln | 20 +++++----- 17 files changed, 65 insertions(+), 74 deletions(-) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite.TestProject => Lab.DAL.TestProject}/EmployeeRepositoryUnitTests.cs (98%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj => Lab.DAL.TestProject/Lab.DAL.TestProject.csproj} (57%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite.TestProject => Lab.DAL.TestProject}/TestEmployeeDbContext.cs (91%) create mode 100644 ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/DefaultDbContextManager.cs (99%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/DomainModel/Employee/InsertOrderRequest.cs (80%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/DomainModel/Employee/NewRequest.cs (86%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EmployeeContextFactory.cs (63%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EmployeeRepository.cs (97%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EntityModel/Employee.cs (94%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EntityModel/EmployeeDbContext.cs (78%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EntityModel/Identity.cs (95%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/EntityModel/OrderHistory.cs (95%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite/Lab.SQLite.csproj => Lab.DAL/Lab.DAL.csproj} (82%) rename ORM/EFCore/Lab.SQLite/{Lab.SQLite => Lab.DAL}/appsettings.json (100%) delete mode 100644 ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs similarity index 98% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 2bc478ee..9d9aaaf1 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -1,8 +1,8 @@ using System; using System.IO; using System.Linq; -using Lab.SQLite.DomainModel.Employee; -using Lab.SQLite.EntityModel; +using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Lab.SQLite.TestProject +namespace Lab.DAL.TestProject { [TestClass] public class EmployeeRepositoryUnitTests diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj similarity index 57% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj rename to ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index 43a4d622..d5c3965e 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/Lab.SQLite.TestProject.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -2,40 +2,31 @@ net5.0 - bin + bin false - - - - - - - - - + + + + + + + + + + - + - - Always - - - - - - - - - - - + + Always + diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/TestEmployeeDbContext.cs similarity index 91% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/TestEmployeeDbContext.cs index ffdf16eb..74a2e32d 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/TestEmployeeDbContext.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/TestEmployeeDbContext.cs @@ -1,12 +1,11 @@ using System; +using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; -namespace Lab.SQLite.EntityModel +namespace Lab.DAL.TestProject { public class TestEmployeeDbContext : DbContext { - private static readonly bool[] s_migrated = {false}; - public virtual DbSet Employees { get; set; } public virtual DbSet Identities { get; set; } diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json new file mode 100644 index 00000000..90427e9f --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=bin\\Lab.DAL.TestProject.db" + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/DefaultDbContextManager.cs similarity index 99% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/DefaultDbContextManager.cs index bc0a0261..4a70778a 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DefaultDbContextManager.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/DefaultDbContextManager.cs @@ -1,12 +1,12 @@ using System; using System.IO; -using Lab.SQLite.EntityModel; +using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Lab.SQLite +namespace Lab.DAL { internal class DefaultDbContextManager { diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs similarity index 80% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs index 0c53a98b..52598da3 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/InsertOrderRequest.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/InsertOrderRequest.cs @@ -1,6 +1,4 @@ -using System; - -namespace Lab.SQLite.DomainModel.Employee +namespace Lab.DAL.DomainModel.Employee { public class InsertOrderRequest { diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/NewRequest.cs similarity index 86% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/NewRequest.cs index 03987557..490f696e 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/DomainModel/Employee/NewRequest.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/DomainModel/Employee/NewRequest.cs @@ -1,7 +1,6 @@ -using System; using System.ComponentModel.DataAnnotations; -namespace Lab.SQLite.DomainModel.Employee +namespace Lab.DAL.DomainModel.Employee { public class NewRequest { diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeContextFactory.cs similarity index 63% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeContextFactory.cs index 1a3a45c4..44a7b638 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeContextFactory.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeContextFactory.cs @@ -1,25 +1,25 @@ using System; -using Lab.SQLite.EntityModel; +using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; -namespace Lab.SQLite +namespace Lab.DAL { public class EmployeeContextFactory : IDesignTimeDbContextFactory { public EmployeeDbContext CreateDbContext(string[] args) { - Console.WriteLine("由設計工具產生 Database,初始化 DbContextOptionsBuilder"); + Console.WriteLine("EmployeeContextFactory - 由設計工具產生 Database,初始化 DbContextOptionsBuilder"); var config = DefaultDbContextManager.Configuration; var connectionString = config.GetConnectionString("DefaultConnection"); - Console.WriteLine($"由 appsettings.json 讀取連線字串為:{connectionString}"); + Console.WriteLine($"EmployeeContextFactory - 讀取 appsettings.json 檔案的讀取連線字串為:{connectionString}"); var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlite(connectionString); - Console.WriteLine($"DbContextOptionsBuilder 設定完成"); + Console.WriteLine($"EmployeeContextFactory - DbContextOptionsBuilder 設定完成"); return new EmployeeDbContext(optionsBuilder.Options); } diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs similarity index 97% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs index a968face..eec3bcfe 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs @@ -1,11 +1,11 @@ using System; using System.Threading; using System.Threading.Tasks; -using Lab.SQLite.DomainModel.Employee; -using Lab.SQLite.EntityModel; +using Lab.DAL.DomainModel.Employee; +using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; -namespace Lab.SQLite +namespace Lab.DAL { public interface IEmployeeRepository { diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Employee.cs similarity index 94% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Employee.cs index 371948bd..ca5252e6 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Employee.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Employee.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.SQLite.EntityModel +namespace Lab.DAL.EntityModel { [Table("Employee")] public class Employee diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/EmployeeDbContext.cs similarity index 78% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/EmployeeDbContext.cs index 842b7602..c8d3832f 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/EmployeeDbContext.cs @@ -1,10 +1,9 @@ using System; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; -namespace Lab.SQLite.EntityModel +namespace Lab.DAL.EntityModel { public class EmployeeDbContext : DbContext { @@ -28,16 +27,17 @@ public EmployeeDbContext(DbContextOptions options) { if (s_migrated[0] == false) { - var memoryOptions = options.FindExtension(); - var sqliteOptionsExtension = options.FindExtension(); - - if (sqliteOptionsExtension != null) - { - Console.WriteLine($"EmployeeDbContext 的連線字串為:{sqliteOptionsExtension.ConnectionString},執行 Migration"); - } + var memoryOptions = options.FindExtension(); if (memoryOptions == null) { + var sqliteOptionsExtension = options.FindExtension(); + + if (sqliteOptionsExtension != null) + { + Console.WriteLine($"EmployeeDbContext 的連線字串為:{sqliteOptionsExtension.ConnectionString},執行 Migration"); + } + this.Database.Migrate(); } diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Identity.cs similarity index 95% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Identity.cs index e5c9949d..5ab55f04 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/Identity.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/Identity.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.SQLite.EntityModel +namespace Lab.DAL.EntityModel { [Table("Identity")] public class Identity diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/OrderHistory.cs similarity index 95% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/OrderHistory.cs index 9b015c57..7a77ca2e 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/EntityModel/OrderHistory.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EntityModel/OrderHistory.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.SQLite.EntityModel +namespace Lab.DAL.EntityModel { [Table("OrderHistory")] public class OrderHistory diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj b/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj similarity index 82% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj rename to ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj index 71f18786..2ad7fe1f 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite/Lab.SQLite.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj @@ -14,10 +14,14 @@ - - <_Parameter1>Lab.SQLite.TestProject + <_Parameter1>Lab.DAL.TestProject + + + Always + + diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.DAL/appsettings.json similarity index 100% rename from ORM/EFCore/Lab.SQLite/Lab.SQLite/appsettings.json rename to ORM/EFCore/Lab.SQLite/Lab.DAL/appsettings.json diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json deleted file mode 100644 index a83c326c..00000000 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.TestProject/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Data Source=Lab.DAL.TestProject.db" - } -} \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln b/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln index 1bb14120..049ab624 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln +++ b/ORM/EFCore/Lab.SQLite/Lab.SQLite.sln @@ -1,8 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SQLite", "Lab.SQLite\Lab.SQLite.csproj", "{ECC70BD1-FB1E-442C-B676-386C11510B43}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL", "Lab.DAL\Lab.DAL.csproj", "{663838CE-1F72-483A-B981-8CCE685911C3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SQLite.TestProject", "Lab.SQLite.TestProject\Lab.SQLite.TestProject.csproj", "{C3821F71-3F5F-4480-8D84-782A55772101}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DAL.TestProject", "Lab.DAL.TestProject\Lab.DAL.TestProject.csproj", "{EFF09A2D-3B94-4BCF-8A38-18186E64F25F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -10,13 +10,13 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ECC70BD1-FB1E-442C-B676-386C11510B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ECC70BD1-FB1E-442C-B676-386C11510B43}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ECC70BD1-FB1E-442C-B676-386C11510B43}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ECC70BD1-FB1E-442C-B676-386C11510B43}.Release|Any CPU.Build.0 = Release|Any CPU - {C3821F71-3F5F-4480-8D84-782A55772101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3821F71-3F5F-4480-8D84-782A55772101}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3821F71-3F5F-4480-8D84-782A55772101}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3821F71-3F5F-4480-8D84-782A55772101}.Release|Any CPU.Build.0 = Release|Any CPU + {663838CE-1F72-483A-B981-8CCE685911C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {663838CE-1F72-483A-B981-8CCE685911C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {663838CE-1F72-483A-B981-8CCE685911C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {663838CE-1F72-483A-B981-8CCE685911C3}.Release|Any CPU.Build.0 = Release|Any CPU + {EFF09A2D-3B94-4BCF-8A38-18186E64F25F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EFF09A2D-3B94-4BCF-8A38-18186E64F25F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EFF09A2D-3B94-4BCF-8A38-18186E64F25F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EFF09A2D-3B94-4BCF-8A38-18186E64F25F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 060119b1156d1ea041aaeacbfa54d427ad552888 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 18:59:41 +0800 Subject: [PATCH 082/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 36 +++-- .../Lab.DAL.TestProject/appsettings.json | 2 +- .../Lab.SQLite/Lab.DAL/EmployeeRepository.cs | 3 +- .../20210416101355_InitialCreate.Designer.cs | 123 ++++++++++++++++++ .../20210416101355_InitialCreate.cs | 78 +++++++++++ .../EmployeeDbContextModelSnapshot.cs | 121 +++++++++++++++++ 6 files changed, 349 insertions(+), 14 deletions(-) create mode 100644 ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs create mode 100644 ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 9d9aaaf1..b99306e9 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -16,7 +16,8 @@ namespace Lab.DAL.TestProject public class EmployeeRepositoryUnitTests { private static readonly DbContextOptions s_employeeContextOptions; - private static readonly string TestDbConnectionString = "Data Source=Lab.DAL.TestProject.db"; + private static readonly string TestDbConnectionString1 = "Data Source=Lab.DAL.TestProject.db"; + private static readonly string TestDbConnectionString2 = "Data Source=Lab.DAL.Injection.db"; static EmployeeRepositoryUnitTests() { @@ -29,8 +30,11 @@ public static void AssemblyCleanup() //刪除測試資料庫 Console.WriteLine("AssemblyCleanup"); - using var db = new TestEmployeeDbContext(TestDbConnectionString); - db.Database.EnsureDeleted(); + using var db1 = new TestEmployeeDbContext(TestDbConnectionString1); + db1.Database.EnsureDeleted(); + + using var db2 = new TestEmployeeDbContext(TestDbConnectionString2); + db2.Database.EnsureDeleted(); } [AssemblyInitialize] @@ -38,8 +42,11 @@ public static void AssemblyInitialize(TestContext context) { //刪除測試資料庫 Console.WriteLine("AssemblyInitialize"); - using var db = new TestEmployeeDbContext(TestDbConnectionString); - db.Database.EnsureDeleted(); + using var db1 = new TestEmployeeDbContext(TestDbConnectionString1); + db1.Database.EnsureDeleted(); + + using var db2 = new TestEmployeeDbContext(TestDbConnectionString2); + db2.Database.EnsureDeleted(); // //建立測試資料庫 // db.Database.Migrate(); @@ -72,7 +79,6 @@ public void 操作真實資料庫_手動取得Repository執行個體() var repository = new EmployeeRepository(); - // var id = Guid.NewGuid(); repository.NewAsync(new NewRequest { Account = "yao", @@ -81,7 +87,8 @@ public void 操作真實資料庫_手動取得Repository執行個體() Age = 18, Remark = "測試案例,持續航向偉大航道" }, "TestUser").Wait(); - using var db = new TestEmployeeDbContext(TestDbConnectionString); + + using var db = new TestEmployeeDbContext(TestDbConnectionString1); var id = db.Employees.FirstOrDefault(p => p.Name == "余小章").Id; //act @@ -119,7 +126,11 @@ public void 操作真實資料庫_注入EmployeeDbContext() var dbContextOptions = host.Services.GetService>(); var repository = host.Services.GetService(); var employeeDbContext = host.Services.GetService(); - // employeeDbContext.Database.Migrate(); + + if (employeeDbContext.Database.CanConnect() == false) + { + employeeDbContext.Database.Migrate(); + } repository.EmployeeDbContext = employeeDbContext; @@ -134,6 +145,7 @@ public void 操作真實資料庫_注入EmployeeDbContext() //assert Assert.AreEqual(2, count); + using var db = new EmployeeDbContext(dbContextOptions); var actual = db.Employees @@ -145,7 +157,6 @@ public void 操作真實資料庫_注入EmployeeDbContext() Assert.AreEqual(actual.Age, 18); Assert.AreEqual(actual.Identity.Account, "yao"); Assert.AreEqual(actual.Identity.Password, "123456"); - db.Database.EnsureDeleted(); } [TestMethod] @@ -156,7 +167,10 @@ public void 操作真實資料庫_預設EmployeeDbContext() DefaultDbContextManager.SetPhysicalDatabase(); var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => { services.AddSingleton(); }); + .ConfigureServices(services => + { + services.AddSingleton(); + }); var host = builder.Build(); var repository = host.Services.GetService(); @@ -172,7 +186,7 @@ public void 操作真實資料庫_預設EmployeeDbContext() //assert Assert.AreEqual(2, count); - using var db = new TestEmployeeDbContext(TestDbConnectionString); + using var db = new TestEmployeeDbContext(TestDbConnectionString1); var actual = db.Employees .Include(p => p.Identity) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json index 90427e9f..a83c326c 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/appsettings.json @@ -1,5 +1,5 @@ { "ConnectionStrings": { - "DefaultConnection": "Data Source=bin\\Lab.DAL.TestProject.db" + "DefaultConnection": "Data Source=Lab.DAL.TestProject.db" } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs index eec3bcfe..cd607a64 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/EmployeeRepository.cs @@ -91,8 +91,7 @@ public async Task NewAsync(NewRequest request, string accessId, CancellationToken cancel = default) { - await using var dbContext = this.EmployeeDbContext; - + using var dbContext = this.EmployeeDbContext; var id = Guid.NewGuid().ToString(); var employeeToDb = new Employee { diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs new file mode 100644 index 00000000..213fa1c9 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs @@ -0,0 +1,123 @@ +// +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Lab.DAL.Migrations +{ + [DbContext(typeof(EmployeeDbContext))] + [Migration("20210416101355_InitialCreate")] + partial class InitialCreate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.5"); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Age") + .HasColumnType("INTEGER"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.Property("Employee_Id") + .HasColumnType("TEXT"); + + b.Property("Account") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Employee_Id"); + + b.ToTable("Identity"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Employee_Id") + .HasColumnType("TEXT"); + + b.Property("Product_Id") + .HasColumnType("TEXT"); + + b.Property("Product_Name") + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("OrderHistory"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithOne("Identity") + .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Navigation("Identity"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs new file mode 100644 index 00000000..1e11c0c3 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Lab.DAL.Migrations +{ + public partial class InitialCreate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Employee", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Age = table.Column(type: "INTEGER", nullable: true), + Remark = table.Column(type: "TEXT", nullable: true), + CreateAt = table.Column(type: "TEXT", nullable: false), + CreateBy = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Employee", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OrderHistory", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Employee_Id = table.Column(type: "TEXT", nullable: true), + Remark = table.Column(type: "TEXT", nullable: true), + Product_Id = table.Column(type: "TEXT", nullable: true), + Product_Name = table.Column(type: "TEXT", nullable: true), + CreateAt = table.Column(type: "TEXT", nullable: false), + CreateBy = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderHistory", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Identity", + columns: table => new + { + Employee_Id = table.Column(type: "TEXT", nullable: false), + Account = table.Column(type: "TEXT", nullable: false), + Password = table.Column(type: "TEXT", nullable: false), + Remark = table.Column(type: "TEXT", nullable: true), + CreateAt = table.Column(type: "TEXT", nullable: false), + CreateBy = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Identity", x => x.Employee_Id); + table.ForeignKey( + name: "FK_Identity_Employee_Employee_Id", + column: x => x.Employee_Id, + principalTable: "Employee", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Identity"); + + migrationBuilder.DropTable( + name: "OrderHistory"); + + migrationBuilder.DropTable( + name: "Employee"); + } + } +} diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs new file mode 100644 index 00000000..7d2d01a4 --- /dev/null +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/EmployeeDbContextModelSnapshot.cs @@ -0,0 +1,121 @@ +// +using System; +using Lab.DAL.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Lab.DAL.Migrations +{ + [DbContext(typeof(EmployeeDbContext))] + partial class EmployeeDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.5"); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Age") + .HasColumnType("INTEGER"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.Property("Employee_Id") + .HasColumnType("TEXT"); + + b.Property("Account") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Employee_Id"); + + b.ToTable("Identity"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.OrderHistory", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreateAt") + .HasColumnType("TEXT"); + + b.Property("CreateBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Employee_Id") + .HasColumnType("TEXT"); + + b.Property("Product_Id") + .HasColumnType("TEXT"); + + b.Property("Product_Name") + .HasColumnType("TEXT"); + + b.Property("Remark") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("OrderHistory"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Identity", b => + { + b.HasOne("Lab.DAL.EntityModel.Employee", "Employee") + .WithOne("Identity") + .HasForeignKey("Lab.DAL.EntityModel.Identity", "Employee_Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Lab.DAL.EntityModel.Employee", b => + { + b.Navigation("Identity"); + }); +#pragma warning restore 612, 618 + } + } +} From 4efc03fcdff4604d0c4aeba7b512867d924f438f Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 16 Apr 2021 22:18:23 +0800 Subject: [PATCH 083/267] refactor --- .../Lab.DAL.TestProject/Lab.DAL.TestProject.csproj | 2 +- ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index d1e58815..4ac56c73 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -16,7 +16,7 @@ - + diff --git a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj index d5ddf873..77c9a56d 100644 --- a/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.VirtualDb/Lab.DAL/Lab.DAL.csproj @@ -8,7 +8,7 @@ - + From bc69418adb470ec3d6c5f05d15450c1779edc4ca Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sat, 17 Apr 2021 12:56:04 +0800 Subject: [PATCH 084/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 28 +++++++++++++++---- .../Lab.DAL.TestProject.csproj | 22 +++++++-------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index b99306e9..e22ccd85 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -105,7 +105,28 @@ public void 操作真實資料庫_手動取得Repository執行個體() } [TestMethod] - public void 操作真實資料庫_注入EmployeeDbContext() + public void 操作真實資料庫_手動實例化EmployeeDbContext() + { + var contextOptions = CreateDbContextOptions(); + using var dbContext = new EmployeeDbContext(contextOptions); + var id = Guid.NewGuid().ToString(); + dbContext.Employees.Add(new Employee() + { + Age = 18, + Id = id, + CreateAt = DateTime.Now, + CreateBy = "test", + Name = "yao" + }); + dbContext.SaveChanges(); + + var actual = dbContext.Employees.AsNoTracking().FirstOrDefault(p => p.Id ==id); + Assert.AreEqual(18,actual.Age); + Assert.AreEqual("yao",actual.Name); + } + + [TestMethod] + public void 操作真實資料庫_由Host注入EmployeeDbContext() { //arrange DefaultDbContextManager.Now = new DateTime(1900, 1, 1); @@ -167,10 +188,7 @@ public void 操作真實資料庫_預設EmployeeDbContext() DefaultDbContextManager.SetPhysicalDatabase(); var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => - { - services.AddSingleton(); - }); + .ConfigureServices(services => { services.AddSingleton(); }); var host = builder.Build(); var repository = host.Services.GetService(); diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index d5c3965e..aebb5b9b 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -7,20 +7,20 @@ - - - - - - - - - - + + + + + + + + + + - + From b4a96a43042dcfad813cd1aa5736a35892e8ad8a Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sat, 17 Apr 2021 22:50:39 +0800 Subject: [PATCH 085/267] add sample --- .../EmployeeRepositoryUnitTests.cs | 108 ++++++++++++++---- 1 file changed, 87 insertions(+), 21 deletions(-) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index e22ccd85..917fc0ab 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -4,6 +4,7 @@ using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -120,49 +121,114 @@ public void 操作真實資料庫_手動實例化EmployeeDbContext() }); dbContext.SaveChanges(); - var actual = dbContext.Employees.AsNoTracking().FirstOrDefault(p => p.Id ==id); - Assert.AreEqual(18,actual.Age); - Assert.AreEqual("yao",actual.Name); + var actual = dbContext.Employees.AsNoTracking().FirstOrDefault(p => p.Id == id); + Assert.AreEqual(18, actual.Age); + Assert.AreEqual("yao", actual.Name); } [TestMethod] - public void 操作真實資料庫_由Host注入EmployeeDbContext() + public void 操作真實資料庫_由Host註冊EmployeeDbContext() { //arrange - DefaultDbContextManager.Now = new DateTime(1900, 1, 1); - - var connectionString = "Data Source=Lab.DAL.Injection.db"; - var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services.AddSingleton(); - services.AddDbContext(builder => + services.AddDbContext((provider, builder) => { + var config = + provider.GetService(); + var connectionString = + config + .GetConnectionString("DefaultConnection"); builder.UseSqlite(connectionString); }); }); var host = builder.Build(); - var dbContextOptions = host.Services.GetService>(); - var repository = host.Services.GetService(); - var employeeDbContext = host.Services.GetService(); + var dbContextOptions = host.Services.GetService>(); + using var dbContext = host.Services.GetService(); - if (employeeDbContext.Database.CanConnect() == false) + //act + var id = Guid.NewGuid().ToString(); + var now = DateTime.Now; + dbContext.Employees.Add(new Employee() + { + Id = id, + Name = "余小章", + Age = 18, + CreateAt = now, + CreateBy = "test user" + }); + dbContext.Identities.Add(new Identity() { - employeeDbContext.Database.Migrate(); - } + Employee_Id = id, + Account = "yao", + Password = "123456", + CreateAt = now, + CreateBy = "test user" + }); + var count = dbContext.SaveChanges(); - repository.EmployeeDbContext = employeeDbContext; + //assert + Assert.AreEqual(2, count); + + using var db = new EmployeeDbContext(dbContextOptions); + + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + } + + [TestMethod] + public void 操作真實資料庫_由Host註冊EmployeeDbContextFactory() + { + //arrange + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services + .AddDbContextFactory((provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config + .GetConnectionString("DefaultConnection"); + builder.UseSqlite(connectionString); + }); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + var dbContextFactory = host.Services.GetService>(); + using var dbContext = dbContextFactory.CreateDbContext(); //act - var count = repository.NewAsync(new NewRequest + var id = Guid.NewGuid().ToString(); + var now = DateTime.Now; + dbContext.Employees.Add(new Employee() { - Account = "yao", - Password = "123456", + Id = id, Name = "余小章", Age = 18, - }, "TestUser").Result; + CreateAt = now, + CreateBy = "test user" + }); + dbContext.Identities.Add(new Identity() + { + Employee_Id = id, + Account = "yao", + Password = "123456", + CreateAt = now, + CreateBy = "test user" + }); + var count = dbContext.SaveChanges(); //assert Assert.AreEqual(2, count); From 278dc5963da8b40c5f386010ce22d8666dcecbcb Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sat, 17 Apr 2021 23:52:47 +0800 Subject: [PATCH 086/267] add tes case --- .../EmployeeRepositoryUnitTests.cs | 167 ++++++++++++++++-- 1 file changed, 148 insertions(+), 19 deletions(-) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index 917fc0ab..de11d7dd 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -133,15 +133,18 @@ public void 操作真實資料庫_由Host註冊EmployeeDbContext() var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services.AddDbContext((provider, builder) => - { - var config = - provider.GetService(); - var connectionString = - config - .GetConnectionString("DefaultConnection"); - builder.UseSqlite(connectionString); - }); + services.AddDbContext( + (provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config.GetConnectionString("DefaultConnection"); + var loggerFactory = provider.GetService(); + builder.UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); }); var host = builder.Build(); @@ -185,6 +188,67 @@ public void 操作真實資料庫_由Host註冊EmployeeDbContext() Assert.AreEqual(actual.Identity.Password, "123456"); } + [TestMethod] + public void 操作真實資料庫_由Host註冊EmployeeDbContextPool() + { + //arrange + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddDbContextPool( + (provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config.GetConnectionString("DefaultConnection"); + var loggerFactory = provider.GetService(); + builder.UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }, 64); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + using var dbContext = host.Services.GetService(); + + //act + var id = Guid.NewGuid().ToString(); + var now = DateTime.Now; + dbContext.Employees.Add(new Employee() + { + Id = id, + Name = "余小章", + Age = 18, + CreateAt = now, + CreateBy = "test user" + }); + dbContext.Identities.Add(new Identity() + { + Employee_Id = id, + Account = "yao", + Password = "123456", + CreateAt = now, + CreateBy = "test user" + }); + var count = dbContext.SaveChanges(); + + //assert + Assert.AreEqual(2, count); + + using var db = new EmployeeDbContext(dbContextOptions); + + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + } [TestMethod] public void 操作真實資料庫_由Host註冊EmployeeDbContextFactory() { @@ -192,16 +256,81 @@ public void 操作真實資料庫_由Host註冊EmployeeDbContextFactory() var builder = Host.CreateDefaultBuilder() .ConfigureServices(services => { - services - .AddDbContextFactory((provider, builder) => - { - var config = - provider.GetService(); - var connectionString = - config - .GetConnectionString("DefaultConnection"); - builder.UseSqlite(connectionString); - }); + services.AddDbContextFactory( + (provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config.GetConnectionString("DefaultConnection"); + var loggerFactory = provider.GetService(); + builder.UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + var dbContextFactory = host.Services.GetService>(); + using var dbContext = dbContextFactory.CreateDbContext(); + + //act + var id = Guid.NewGuid().ToString(); + var now = DateTime.Now; + dbContext.Employees.Add(new Employee() + { + Id = id, + Name = "余小章", + Age = 18, + CreateAt = now, + CreateBy = "test user" + }); + dbContext.Identities.Add(new Identity() + { + Employee_Id = id, + Account = "yao", + Password = "123456", + CreateAt = now, + CreateBy = "test user" + }); + var count = dbContext.SaveChanges(); + + //assert + Assert.AreEqual(2, count); + + using var db = new EmployeeDbContext(dbContextOptions); + + var actual = db.Employees + .Include(p => p.Identity) + .AsNoTracking() + .FirstOrDefault(); + + Assert.AreEqual(actual.Name, "余小章"); + Assert.AreEqual(actual.Age, 18); + Assert.AreEqual(actual.Identity.Account, "yao"); + Assert.AreEqual(actual.Identity.Password, "123456"); + } + + [TestMethod] + public void 操作真實資料庫_由Host註冊EmployeeDbContextPoolFactory() + { + //arrange + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddPooledDbContextFactory( + (provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config.GetConnectionString("DefaultConnection"); + var loggerFactory = provider.GetService(); + builder.UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + ; + },64); }); var host = builder.Build(); From d7fd4be8ee59c58c907b66bbd057d87fbc48f8a1 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sun, 18 Apr 2021 20:18:59 +0800 Subject: [PATCH 087/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 56 ++++++++++++++++++- ... 20210418023303_InitialCreate.Designer.cs} | 2 +- ...ate.cs => 20210418023303_InitialCreate.cs} | 0 3 files changed, 56 insertions(+), 2 deletions(-) rename ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/{20210416101355_InitialCreate.Designer.cs => 20210418023303_InitialCreate.Designer.cs} (98%) rename ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/{20210416101355_InitialCreate.cs => 20210418023303_InitialCreate.cs} (100%) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index de11d7dd..fc81c1c9 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Threading.Tasks; using Lab.DAL.DomainModel.Employee; using Lab.DAL.EntityModel; using Microsoft.EntityFrameworkCore; @@ -126,6 +127,58 @@ public void 操作真實資料庫_手動實例化EmployeeDbContext() Assert.AreEqual("yao", actual.Name); } + [TestMethod] + public async Task 操作真實資料庫_() + { + //arrange + var builder = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddDbContext( + (provider, builder) => + { + var config = + provider.GetService(); + var connectionString = + config.GetConnectionString("DefaultConnection"); + var loggerFactory = provider.GetService(); + builder.UseSqlite(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + }); + var host = builder.Build(); + + var dbContextOptions = host.Services.GetService>(); + await using var dbContext = host.Services.GetService(); + + //act + var id = Guid.NewGuid().ToString(); + var now = DateTime.Now; + dbContext.Employees.Add(new Employee() + { + Id = id, + Name = "余小章", + Age = 18, + CreateAt = now, + CreateBy = "test user" + }); + dbContext.Identities.Add(new Identity() + { + Employee_Id = id, + Account = "yao", + Password = "123456", + CreateAt = now, + CreateBy = "test user" + }); + var count = await dbContext.SaveChangesAsync(); + + // var employeesTask = dbContext.Employees.OrderBy(p=>p.Id).AsNoTracking().LastAsync(); + var employeesTask = dbContext.Employees.AsNoTracking().LastAsync(); + var identitiesTask = dbContext.Identities.OrderBy(p => p.Employee_Id).AsNoTracking().LastAsync(); + await Task.WhenAll(employeesTask, identitiesTask); + } + [TestMethod] public void 操作真實資料庫_由Host註冊EmployeeDbContext() { @@ -249,6 +302,7 @@ public void 操作真實資料庫_由Host註冊EmployeeDbContextPool() Assert.AreEqual(actual.Identity.Account, "yao"); Assert.AreEqual(actual.Identity.Password, "123456"); } + [TestMethod] public void 操作真實資料庫_由Host註冊EmployeeDbContextFactory() { @@ -330,7 +384,7 @@ public void 操作真實資料庫_由Host註冊EmployeeDbContextPoolFactory() builder.UseSqlite(connectionString) .UseLoggerFactory(loggerFactory) ; - },64); + }, 64); }); var host = builder.Build(); diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210418023303_InitialCreate.Designer.cs similarity index 98% rename from ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210418023303_InitialCreate.Designer.cs index 213fa1c9..55cf0c0e 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.Designer.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210418023303_InitialCreate.Designer.cs @@ -9,7 +9,7 @@ namespace Lab.DAL.Migrations { [DbContext(typeof(EmployeeDbContext))] - [Migration("20210416101355_InitialCreate")] + [Migration("20210418023303_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210418023303_InitialCreate.cs similarity index 100% rename from ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210416101355_InitialCreate.cs rename to ORM/EFCore/Lab.SQLite/Lab.DAL/Migrations/20210418023303_InitialCreate.cs From 6a362dbbeadd30d6998b67c7e939f35c7c47dfca Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Sun, 18 Apr 2021 20:27:41 +0800 Subject: [PATCH 088/267] refactor --- .../EmployeeRepositoryUnitTests.cs | 52 ------------------- .../Lab.DAL.TestProject.csproj | 19 +++---- 2 files changed, 8 insertions(+), 63 deletions(-) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs index fc81c1c9..647c94ae 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/EmployeeRepositoryUnitTests.cs @@ -127,58 +127,6 @@ public void 操作真實資料庫_手動實例化EmployeeDbContext() Assert.AreEqual("yao", actual.Name); } - [TestMethod] - public async Task 操作真實資料庫_() - { - //arrange - var builder = Host.CreateDefaultBuilder() - .ConfigureServices(services => - { - services.AddDbContext( - (provider, builder) => - { - var config = - provider.GetService(); - var connectionString = - config.GetConnectionString("DefaultConnection"); - var loggerFactory = provider.GetService(); - builder.UseSqlite(connectionString) - .UseLoggerFactory(loggerFactory) - ; - }); - }); - var host = builder.Build(); - - var dbContextOptions = host.Services.GetService>(); - await using var dbContext = host.Services.GetService(); - - //act - var id = Guid.NewGuid().ToString(); - var now = DateTime.Now; - dbContext.Employees.Add(new Employee() - { - Id = id, - Name = "余小章", - Age = 18, - CreateAt = now, - CreateBy = "test user" - }); - dbContext.Identities.Add(new Identity() - { - Employee_Id = id, - Account = "yao", - Password = "123456", - CreateAt = now, - CreateBy = "test user" - }); - var count = await dbContext.SaveChangesAsync(); - - // var employeesTask = dbContext.Employees.OrderBy(p=>p.Id).AsNoTracking().LastAsync(); - var employeesTask = dbContext.Employees.AsNoTracking().LastAsync(); - var identitiesTask = dbContext.Identities.OrderBy(p => p.Employee_Id).AsNoTracking().LastAsync(); - await Task.WhenAll(employeesTask, identitiesTask); - } - [TestMethod] public void 操作真實資料庫_由Host註冊EmployeeDbContext() { diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj index aebb5b9b..29435055 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL.TestProject/Lab.DAL.TestProject.csproj @@ -7,20 +7,17 @@ - - - - - - - - - - + + + + + + + - + From d0b2392874b880610822995274cdd4ffb5df8181 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 30 Apr 2021 19:03:10 +0800 Subject: [PATCH 089/267] remove reference package --- ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj b/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj index 2ad7fe1f..e7762bff 100644 --- a/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj +++ b/ORM/EFCore/Lab.SQLite/Lab.DAL/Lab.DAL.csproj @@ -10,7 +10,6 @@ - From 74e63a0bb02fce6dbe0503889ef1337ac01991b6 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 10 May 2021 14:49:32 +0800 Subject: [PATCH 090/267] =?UTF-8?q?fix:=20DefaultDependencyResolver.Dispos?= =?UTF-8?q?e()=20=E4=B8=8D=E6=9C=83=E8=AA=BF=E7=94=A8=E7=89=A9=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=20Dispose()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App_Start/DefaultDependencyResolver.cs | 19 ++++++++++--------- DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs | 6 ++++-- .../WebApiNet48/Message/LogMessager.cs | 5 +++++ .../WebApiNet48/Message/MachineMessager.cs | 5 +++++ .../WebApiNet48/Message/MultiMessager.cs | 5 +++++ 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs index 6dc35633..e9e64035 100644 --- a/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs +++ b/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs @@ -7,33 +7,34 @@ namespace WebApiNet48 { public class DefaultDependencyResolver : IDependencyResolver { - protected IServiceProvider ServiceProvider { get; set; } + private readonly IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; - public DefaultDependencyResolver(IServiceProvider serviceProvider) + public DefaultDependencyResolver(IServiceProvider serviceProvider, IServiceScope serviceScope = null) { - this.ServiceProvider = serviceProvider; + this._serviceProvider = serviceProvider; + this._serviceScope = serviceScope; } public object GetService(Type serviceType) { - return this.ServiceProvider.GetService(serviceType); + return this._serviceProvider.GetService(serviceType); } public IEnumerable GetServices(Type serviceType) { - return this.ServiceProvider.GetServices(serviceType); + return this._serviceProvider.GetServices(serviceType); } public IDependencyScope BeginScope() { - return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); + this._serviceScope = this._serviceProvider.CreateScope(); + return new DefaultDependencyResolver(this._serviceScope.ServiceProvider,this._serviceScope); } public void Dispose() { - // you can implement this interface just when you use .net core 2.0 - // this.ServiceProvider.Dispose(); - ((ServiceProvider) this.ServiceProvider).Dispose(); + this._serviceScope?.Dispose(); } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs index 137ae92c..9883346c 100644 --- a/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs +++ b/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs @@ -1,6 +1,8 @@ -namespace WebApiNet48 +using System; + +namespace WebApiNet48 { - public interface IMessager + public interface IMessager:IDisposable { string OperationId { get; } } diff --git a/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs index 91e273eb..a73f42f4 100644 --- a/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs +++ b/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs @@ -5,5 +5,10 @@ namespace WebApiNet48 internal class LogMessager : IMessager { public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs index 39e04bc5..8aed0c19 100644 --- a/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs +++ b/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs @@ -5,5 +5,10 @@ namespace WebApiNet48 internal class MachineMessager : IMessager { public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs index 17d01e6c..7e3edf7b 100644 --- a/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs +++ b/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs @@ -5,5 +5,10 @@ namespace WebApiNet48 public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager { public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } } } \ No newline at end of file From 16c77af4233930c4c13fca06f70a475bf5142714 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 10 May 2021 15:07:02 +0800 Subject: [PATCH 091/267] =?UTF-8?q?refactor:=20=E8=A7=80=E5=AF=9F=20mvc5?= =?UTF-8?q?=20=E5=B0=88=E6=A1=88=E7=9A=84=20DI=20=E6=9C=89=E6=B2=92?= =?UTF-8?q?=E6=9C=89=E6=AD=A3=E5=B8=B8=E7=9A=84=E5=91=BC=E5=8F=AB=20IMessa?= =?UTF-8?q?ge.Dispose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Mvc5Net48/App_Start/DefaultDependencyResolver.cs | 1 - DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs | 6 ++++-- DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs | 5 +++++ DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs | 4 ++++ DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs | 4 ++++ DI/Lab.MsDI/Mvc5Net48/Web.config | 8 ++++++++ 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs index 18630899..ab391905 100644 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs @@ -8,7 +8,6 @@ namespace Mvc5Net48 { internal class DefaultDependencyResolver : IDependencyResolver { - public object GetService(Type serviceType) { if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs index 8f3da6d6..e3bda957 100644 --- a/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs +++ b/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs @@ -1,6 +1,8 @@ -namespace Mvc5Net48.Message +using System; + +namespace Mvc5Net48.Message { - public interface IMessager + public interface IMessager:IDisposable { string OperationId { get; } } diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs index 85dc6327..b40b1319 100644 --- a/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs +++ b/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs @@ -5,5 +5,10 @@ namespace Mvc5Net48.Message internal class LogMessager : IMessager { public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs index 84fa31b8..8d91ec1d 100644 --- a/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs +++ b/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs @@ -5,5 +5,9 @@ namespace Mvc5Net48.Message internal class MachineMessager : IMessager { public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs index 1b155f0c..d2206ff8 100644 --- a/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs +++ b/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs @@ -5,5 +5,9 @@ namespace Mvc5Net48.Message public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager { public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.config b/DI/Lab.MsDI/Mvc5Net48/Web.config index 417fbf99..58fb7220 100644 --- a/DI/Lab.MsDI/Mvc5Net48/Web.config +++ b/DI/Lab.MsDI/Mvc5Net48/Web.config @@ -28,6 +28,14 @@ + + + + + + + + From 596eb7e2651a810c0edb2e879fa5c91b81a04b43 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Mon, 10 May 2021 15:11:32 +0800 Subject: [PATCH 092/267] =?UTF-8?q?refactor:=20=E8=A7=80=E5=AF=9F=20asp.ne?= =?UTF-8?q?t=20core=20=E5=B0=88=E6=A1=88=E7=9A=84=20DI=20=E6=9C=89?= =?UTF-8?q?=E6=B2=92=E6=9C=89=E6=AD=A3=E5=B8=B8=E7=9A=84=E5=91=BC=E5=8F=AB?= =?UTF-8?q?=20IMessage.Dispose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs | 6 ++++-- DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs | 5 +++++ DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs | 4 ++++ DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs | 5 +++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs index 07685fa7..479144a9 100644 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs @@ -1,6 +1,8 @@ -namespace WebApiNetCore31 +using System; + +namespace WebApiNetCore31 { - public interface IMessager + public interface IMessager:IDisposable { string OperationId { get; } } diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs index 633adcba..9819ac66 100644 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs @@ -5,5 +5,10 @@ namespace WebApiNetCore31 internal class LogMessager : IMessager { public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs index d3e4038f..ed49b5dd 100644 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs @@ -5,5 +5,9 @@ namespace WebApiNetCore31 internal class MachineMessager : IMessager { public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } } } \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs index e4eb78a9..68182ed0 100644 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs @@ -5,5 +5,10 @@ namespace WebApiNetCore31 public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager { public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } } } \ No newline at end of file From 1d90cf803738d8d0f6c5ef07352c0e9362facf9d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 20 May 2021 10:22:55 +0800 Subject: [PATCH 093/267] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=20func=20nam?= =?UTF-8?q?e=20=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DI/Lab.MultipleImpl/Client/Client.csproj | 21 +++++++ DI/Lab.MultipleImpl/Client/UnitTest1.cs | 56 +++++++++++++++++++ DI/Lab.MultipleImpl/Lab.MultipleImpl.sln | 22 ++++++++ .../Server/Controllers/DefaultController.cs | 31 ++++++++++ .../Controllers/WeatherForecastController.cs | 39 +++++++++++++ .../Server/File/FileProvider.cs | 14 +++++ .../Server/File/IFileProvider.cs | 7 +++ .../Server/File/ZipFileProvider.cs | 14 +++++ DI/Lab.MultipleImpl/Server/Program.cs | 23 ++++++++ .../Server/Properties/launchSettings.json | 31 ++++++++++ DI/Lab.MultipleImpl/Server/Server.csproj | 19 +++++++ .../Server/ServiceProviderExtension.cs | 14 +++++ DI/Lab.MultipleImpl/Server/Startup.cs | 52 +++++++++++++++++ DI/Lab.MultipleImpl/Server/WeatherForecast.cs | 15 +++++ .../Server/appsettings.Development.json | 9 +++ DI/Lab.MultipleImpl/Server/appsettings.json | 10 ++++ 16 files changed, 377 insertions(+) create mode 100644 DI/Lab.MultipleImpl/Client/Client.csproj create mode 100644 DI/Lab.MultipleImpl/Client/UnitTest1.cs create mode 100644 DI/Lab.MultipleImpl/Lab.MultipleImpl.sln create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/FileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/IFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/Program.cs create mode 100644 DI/Lab.MultipleImpl/Server/Properties/launchSettings.json create mode 100644 DI/Lab.MultipleImpl/Server/Server.csproj create mode 100644 DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs create mode 100644 DI/Lab.MultipleImpl/Server/Startup.cs create mode 100644 DI/Lab.MultipleImpl/Server/WeatherForecast.cs create mode 100644 DI/Lab.MultipleImpl/Server/appsettings.Development.json create mode 100644 DI/Lab.MultipleImpl/Server/appsettings.json diff --git a/DI/Lab.MultipleImpl/Client/Client.csproj b/DI/Lab.MultipleImpl/Client/Client.csproj new file mode 100644 index 00000000..5dfa1709 --- /dev/null +++ b/DI/Lab.MultipleImpl/Client/Client.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs new file mode 100644 index 00000000..ba1277ba --- /dev/null +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -0,0 +1,56 @@ +using System; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Server; + +namespace Client +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(UseFuncName) + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "default/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider",response); + } + + private static void UseFuncName(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton>(provider => + key => + { + switch (key) + { + case "zip": + return provider + .GetService(); + case "file": + return provider + .GetService(); + default: + throw new NotSupportedException(); + } + }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln new file mode 100644 index 00000000..748e5666 --- /dev/null +++ b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{DAE7F74D-E847-4B2F-8930-59AF2698FD1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.Build.0 = Release|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs new file mode 100644 index 00000000..79efc330 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + + private readonly ILogger _logger; + + public DefaultController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + [Route("{type}")] + public IActionResult Get(string type) + { + var fileProvider = this.HttpContext.RequestServices.GetService(type); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs b/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..69c4d675 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/FileProvider.cs b/DI/Lab.MultipleImpl/Server/File/FileProvider.cs new file mode 100644 index 00000000..06045138 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/FileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public class FileProvider : IFileProvider + { + public string Print() + { + var msg = "FileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs new file mode 100644 index 00000000..7c69a1ae --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs @@ -0,0 +1,7 @@ +namespace Server +{ + public interface IFileProvider + { + string Print(); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs new file mode 100644 index 00000000..804a286a --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public class ZipFileProvider : IFileProvider + { + public string Print() + { + var msg = "ZipFileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Program.cs b/DI/Lab.MultipleImpl/Server/Program.cs new file mode 100644 index 00000000..a8f15ae6 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Server +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json b/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json new file mode 100644 index 00000000..f822a28b --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59369", + "sslPort": 44389 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Server": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj b/DI/Lab.MultipleImpl/Server/Server.csproj new file mode 100644 index 00000000..743b9e0a --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Server.csproj @@ -0,0 +1,19 @@ + + + + net5.0 + + + + + + + + + + + + + + + diff --git a/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs new file mode 100644 index 00000000..601138e2 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public static class ServiceProviderExtension + { + public static T GetService(this IServiceProvider provider, string name) + { + var pool = (Func) provider.GetService(typeof(Func)); + return (T) pool(name); + } + } + +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Startup.cs b/DI/Lab.MultipleImpl/Server/Startup.cs new file mode 100644 index 00000000..e7ecb450 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Startup.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; + +namespace Server +{ + 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.AddControllers(); + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/WeatherForecast.cs b/DI/Lab.MultipleImpl/Server/WeatherForecast.cs new file mode 100644 index 00000000..36e011e2 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace Server +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/appsettings.Development.json b/DI/Lab.MultipleImpl/Server/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/DI/Lab.MultipleImpl/Server/appsettings.json b/DI/Lab.MultipleImpl/Server/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} From 76a30ca8b7aca2f681da17fc032de4efb84fbc3a Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 20 May 2021 10:24:43 +0800 Subject: [PATCH 094/267] refactor: test case name --- DI/Lab.MultipleImpl/Client/UnitTest1.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs index ba1277ba..63adb3e1 100644 --- a/DI/Lab.MultipleImpl/Client/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -12,7 +12,7 @@ namespace Client public class UnitTest1 { [TestMethod] - public void TestMethod1() + public void 注入FuncName() { using var server = new TestServer(WebHost.CreateDefaultBuilder() @@ -29,7 +29,7 @@ public void TestMethod1() response.EnsureSuccessStatusCode(); var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider",response); + Assert.AreEqual("ZipFileProvider",result); } private static void UseFuncName(IServiceCollection services) From 69c21ed4992ecf5f7ee8319f3ee29b6c3dcf1cdb Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 20 May 2021 11:34:13 +0800 Subject: [PATCH 095/267] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20Unity=20?= =?UTF-8?q?=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DI/Lab.MultipleImpl/Client/Client.csproj | 1 + DI/Lab.MultipleImpl/Client/UnitTest1.cs | 41 ++++++++++++++++++- .../Controllers/UnityDefaultController.cs | 39 ++++++++++++++++++ DI/Lab.MultipleImpl/Server/Server.csproj | 1 + 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs diff --git a/DI/Lab.MultipleImpl/Client/Client.csproj b/DI/Lab.MultipleImpl/Client/Client.csproj index 5dfa1709..e7dd30b6 100644 --- a/DI/Lab.MultipleImpl/Client/Client.csproj +++ b/DI/Lab.MultipleImpl/Client/Client.csproj @@ -12,6 +12,7 @@ + diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs index 63adb3e1..fee0b493 100644 --- a/DI/Lab.MultipleImpl/Client/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -5,12 +5,39 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Server; +using Unity; +using Unity.Microsoft.DependencyInjection; namespace Client { [TestClass] public class UnitTest1 { + [TestMethod] + public void Unity注入ServiceName() + { + var unityContainer = new UnityContainer(); + ConfigureContainer(unityContainer); + + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + .UseUnityServiceProvider(unityContainer) + .ConfigureServices(UseUnityController) + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "UnityDefault"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + [TestMethod] public void 注入FuncName() { @@ -29,7 +56,13 @@ public void 注入FuncName() response.EnsureSuccessStatusCode(); var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider",result); + Assert.AreEqual("ZipFileProvider", result); + } + + private static void ConfigureContainer(IUnityContainer container) + { + container.RegisterType("zip"); + container.RegisterType("file"); } private static void UseFuncName(IServiceCollection services) @@ -52,5 +85,11 @@ private static void UseFuncName(IServiceCollection services) } }); } + + private static void UseUnityController(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + } } } \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs b/DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs new file mode 100644 index 00000000..2e437bea --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Unity; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnityDefaultController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public UnityDefaultController(ILogger logger, + // [Dependency("zip")] IFileProvider fileProvider) + // { + // this._logger = logger; + // this._fileProvider = fileProvider; + // } + public UnityDefaultController(ILogger logger) + { + this._logger = logger; + + // this._fileProvider = fileProvider; + } + + [HttpGet] + public IActionResult Get() + { + var serviceProvider = this.HttpContext.RequestServices; + var unityServiceProvider = (Unity.Microsoft.DependencyInjection.ServiceProvider) serviceProvider; + var unityContainer = (UnityContainer) unityServiceProvider; + var fileProvider = unityContainer.Resolve("zip"); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj b/DI/Lab.MultipleImpl/Server/Server.csproj index 743b9e0a..d456ed92 100644 --- a/DI/Lab.MultipleImpl/Server/Server.csproj +++ b/DI/Lab.MultipleImpl/Server/Server.csproj @@ -6,6 +6,7 @@ + From 31cf8cef69bc18bf6285938f8552ff54bf3794fc Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 10:46:28 +0800 Subject: [PATCH 096/267] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Autofac=20?= =?UTF-8?q?test=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DI/Lab.MultipleImpl/Client/Client.csproj | 1 + DI/Lab.MultipleImpl/Client/UnitTest1.cs | 33 ++++++++++ DI/Lab.MultipleImpl/Server/AutofacStartup.cs | 63 +++++++++++++++++++ .../Server/Controllers/AutofacController.cs | 35 +++++++++++ ...efaultController.cs => UnityController.cs} | 18 ++---- .../Server/DependencyConfig.cs | 35 +++++++++++ DI/Lab.MultipleImpl/Server/Program.cs | 2 + DI/Lab.MultipleImpl/Server/Server.csproj | 1 + 8 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 DI/Lab.MultipleImpl/Server/AutofacStartup.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs rename DI/Lab.MultipleImpl/Server/Controllers/{UnityDefaultController.cs => UnityController.cs} (57%) create mode 100644 DI/Lab.MultipleImpl/Server/DependencyConfig.cs diff --git a/DI/Lab.MultipleImpl/Client/Client.csproj b/DI/Lab.MultipleImpl/Client/Client.csproj index e7dd30b6..e7e23018 100644 --- a/DI/Lab.MultipleImpl/Client/Client.csproj +++ b/DI/Lab.MultipleImpl/Client/Client.csproj @@ -7,6 +7,7 @@ + diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs index fee0b493..20e25d88 100644 --- a/DI/Lab.MultipleImpl/Client/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -1,10 +1,14 @@ using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Autofac.Features.AttributeFilters; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Server; +using Server.Controllers; using Unity; using Unity.Microsoft.DependencyInjection; @@ -13,6 +17,28 @@ namespace Client [TestClass] public class UnitTest1 { + [TestMethod] + public void Autofac注入ServiceName() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureServices(services => { services.AddAutofac(); }) + .UseStartup() + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "autofac"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + [TestMethod] public void Unity注入ServiceName() { @@ -59,6 +85,13 @@ public void 注入FuncName() Assert.AreEqual("ZipFileProvider", result); } + private static void ConfigureContainer(ContainerBuilder builder) + { + // builder.RegisterType().Keyed("file"); + // builder.RegisterType().Keyed("zip"); + // builder.RegisterType().WithAttributeFiltering(); + } + private static void ConfigureContainer(IUnityContainer container) { container.RegisterType("zip"); diff --git a/DI/Lab.MultipleImpl/Server/AutofacStartup.cs b/DI/Lab.MultipleImpl/Server/AutofacStartup.cs new file mode 100644 index 00000000..1e93de1e --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/AutofacStartup.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Autofac; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using Server.Controllers; + +namespace Server +{ + public class AutofacStartup + { + public AutofacStartup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureContainer(ContainerBuilder builder) + { + builder.RegisterType().Keyed("file"); + builder.RegisterType().Keyed("zip"); + builder.RegisterType().WithAttributeFiltering(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs new file mode 100644 index 00000000..45debe20 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AutofacController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public AutofacDefaultController(ILogger logger) + // { + // this._logger = logger; + // } + + public AutofacController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + this._fileProvider.Print(); + } + + [HttpGet] + public IActionResult Get() + { + return this.Ok(this._fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs b/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs similarity index 57% rename from DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs rename to DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs index 2e437bea..44910a17 100644 --- a/DI/Lab.MultipleImpl/Server/Controllers/UnityDefaultController.cs +++ b/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs @@ -6,23 +6,17 @@ namespace Server.Controllers { [ApiController] [Route("[controller]")] - public class UnityDefaultController : ControllerBase + public class UnityController : ControllerBase { private readonly IFileProvider _fileProvider; - private readonly ILogger _logger; + private readonly ILogger _logger; - // public UnityDefaultController(ILogger logger, - // [Dependency("zip")] IFileProvider fileProvider) - // { - // this._logger = logger; - // this._fileProvider = fileProvider; - // } - public UnityDefaultController(ILogger logger) + public UnityController(ILogger logger, + [Dependency("zip")] IFileProvider fileProvider) { - this._logger = logger; - - // this._fileProvider = fileProvider; + this._logger = logger; + this._fileProvider = fileProvider; } [HttpGet] diff --git a/DI/Lab.MultipleImpl/Server/DependencyConfig.cs b/DI/Lab.MultipleImpl/Server/DependencyConfig.cs new file mode 100644 index 00000000..b6df11a4 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/DependencyConfig.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +namespace Server +{ + public class DependencyConfig + { + public static void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices() + ; + } + public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Program.cs b/DI/Lab.MultipleImpl/Server/Program.cs index a8f15ae6..c083a4aa 100644 --- a/DI/Lab.MultipleImpl/Server/Program.cs +++ b/DI/Lab.MultipleImpl/Server/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -18,6 +19,7 @@ public static void Main(string[] args) public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } } \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj b/DI/Lab.MultipleImpl/Server/Server.csproj index d456ed92..153ed02c 100644 --- a/DI/Lab.MultipleImpl/Server/Server.csproj +++ b/DI/Lab.MultipleImpl/Server/Server.csproj @@ -5,6 +5,7 @@ + From b69506035e40f5b88c60ec16cddc02abbeb12c60 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 10:59:48 +0800 Subject: [PATCH 097/267] fix --- DI/Lab.MultipleImpl/Client/UnitTest1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs index 20e25d88..0dcc77b8 100644 --- a/DI/Lab.MultipleImpl/Client/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -56,7 +56,7 @@ public void Unity注入ServiceName() }; var client = server.CreateClient(); - var url = "UnityDefault"; + var url = "unity"; var response = client.GetAsync(url).Result; response.EnsureSuccessStatusCode(); From 3735188b5bcfa09d2518c055822e1f52d442891c Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 11:17:32 +0800 Subject: [PATCH 098/267] add NET5.TestProject.csproj --- DI/Lab.MultipleImpl/Lab.MultipleImpl.sln | 6 ++ .../NET5.TestProject/AutofacStartup.cs | 53 ++++++++++ .../Controllers/AutofacController.cs | 35 +++++++ .../Controllers/DefaultController.cs | 27 +++++ .../Controllers/UnityController.cs | 34 +++++++ .../Controllers/WeatherForecastController.cs | 38 +++++++ .../NET5.TestProject/File/FileProvider.cs | 14 +++ .../NET5.TestProject/File/IFileProvider.cs | 7 ++ .../NET5.TestProject/File/ZipFileProvider.cs | 14 +++ .../NET5.TestProject/FuncStartup.cs | 63 ++++++++++++ .../NET5.TestProject/NET5.TestProject.csproj | 27 +++++ .../ServiceProviderExtension.cs | 15 +++ .../NET5.TestProject/Startup.cs | 41 ++++++++ .../NET5.TestProject/UnitTest1.cs | 99 +++++++++++++++++++ .../NET5.TestProject/WeatherForecast.cs | 15 +++ .../NET5.TestProject/appsettings.json | 10 ++ 16 files changed, 498 insertions(+) create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json diff --git a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln index 748e5666..8a158dd3 100644 --- a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln +++ b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NET5.TestProject", "NET5.TestProject\NET5.TestProject.csproj", "{A433C8F8-3B75-412E-955F-287639C55C5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.Build.0 = Release|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs new file mode 100644 index 00000000..46dd9d76 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs @@ -0,0 +1,53 @@ +using Autofac; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NET5.TestProject.Controllers; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class AutofacStartup + { + public AutofacStartup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureContainer(ContainerBuilder builder) + { + builder.RegisterType().Keyed("file"); + builder.RegisterType().Keyed("zip"); + builder.RegisterType().WithAttributeFiltering(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs new file mode 100644 index 00000000..29da9a47 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs @@ -0,0 +1,35 @@ +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AutofacController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public AutofacDefaultController(ILogger logger) + // { + // this._logger = logger; + // } + + public AutofacController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + this._fileProvider.Print(); + } + + [HttpGet] + public IActionResult Get() + { + return this.Ok(this._fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs new file mode 100644 index 00000000..31f21928 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class FuncController : ControllerBase + { + private readonly ILogger _logger; + + public FuncController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet] + [Route("{type}")] + public IActionResult Get(string type) + { + var fileProvider = this.HttpContext.RequestServices.GetService(type); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs new file mode 100644 index 00000000..2a748fc6 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; +using Unity; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnityController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + public UnityController(ILogger logger, + [Dependency("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + + [HttpGet] + public IActionResult Get() + { + var serviceProvider = this.HttpContext.RequestServices; + var unityServiceProvider = (Unity.Microsoft.DependencyInjection.ServiceProvider) serviceProvider; + var unityContainer = (UnityContainer) unityServiceProvider; + var fileProvider = unityContainer.Resolve("zip"); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..2dc89a7f --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs new file mode 100644 index 00000000..f9ea7bea --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace NET5.TestProject.File +{ + public class FileProvider : IFileProvider + { + public string Print() + { + var msg = "FileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs new file mode 100644 index 00000000..e72e465a --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs @@ -0,0 +1,7 @@ +namespace NET5.TestProject.File +{ + public interface IFileProvider + { + string Print(); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs new file mode 100644 index 00000000..058d383d --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace NET5.TestProject.File +{ + public class ZipFileProvider : IFileProvider + { + public string Print() + { + var msg = "ZipFileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs new file mode 100644 index 00000000..5be65458 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class FuncStartup + { + public FuncStartup(IConfiguration configuration) + { + this.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.AddControllers(); + UseFuncName(services); + } + private static void UseFuncName(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton>(provider => + key => + { + switch (key) + { + case "zip": + return provider + .GetService(); + case "file": + return provider + .GetService(); + default: + throw new NotSupportedException(); + } + }); + } + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj new file mode 100644 index 00000000..c363eb46 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj @@ -0,0 +1,27 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + true + PreserveNewest + PreserveNewest + + + + diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs new file mode 100644 index 00000000..5384bc0f --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs @@ -0,0 +1,15 @@ +using System; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public static class ServiceProviderExtension + { + public static T GetService(this IServiceProvider provider, string name) + { + var pool = (Func) provider.GetService(typeof(Func)); + return (T) pool(name); + } + } + +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs new file mode 100644 index 00000000..2bad24fc --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace NET5.TestProject +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + this.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.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs new file mode 100644 index 00000000..8f09b0a1 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -0,0 +1,99 @@ +using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NET5.TestProject.File; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace NET5.TestProject +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void Autofac注入ServiceName() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureServices(services => { services.AddAutofac(); }) + .UseStartup() + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "autofac"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void Unity注入ServiceName() + { + var unityContainer = new UnityContainer(); + ConfigureContainer(unityContainer); + + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + .UseUnityServiceProvider(unityContainer) + .ConfigureServices(UseUnityController) + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "unity"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void 注入FuncName() + { + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "default/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + private static void ConfigureContainer(IUnityContainer container) + { + container.RegisterType("zip"); + container.RegisterType("file"); + } + + + + private static void UseUnityController(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs b/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs new file mode 100644 index 00000000..1eb10ce9 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace NET5.TestProject +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (this.TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json b/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} From d89c69b93b7ccfd410b7786c2adeea19ae79fc65 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 12:22:43 +0800 Subject: [PATCH 099/267] refactor --- ...DefaultController.cs => FuncController.cs} | 0 .../NET5.TestProject/FileAdapter.cs | 19 +++++++++++++++++++ .../NET5.TestProject/NET5.TestProject.csproj | 1 + 3 files changed, 20 insertions(+) rename DI/Lab.MultipleImpl/NET5.TestProject/Controllers/{DefaultController.cs => FuncController.cs} (100%) create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs similarity index 100% rename from DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs rename to DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs new file mode 100644 index 00000000..41034a00 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs @@ -0,0 +1,19 @@ +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class FileAdapter + { + private readonly IFileProvider _fileProvider; + + public FileAdapter(IFileProvider fileProvider) + { + this._fileProvider = fileProvider; + } + + public string Get() + { + return this._fileProvider.Print(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj index c363eb46..1c284244 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj +++ b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj @@ -7,6 +7,7 @@ + From ff8e202538de87327086e6f99e9a09e6fcd24e8c Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 13:42:05 +0800 Subject: [PATCH 100/267] new manual di register --- .../Controllers/DefaultController.cs | 30 ++++++++++++++ .../NET5.TestProject/UnitTest1.cs | 39 +++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs new file mode 100644 index 00000000..fad0093e --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private readonly ILogger _logger; + private readonly IFileProvider _fileProvider; + + public DefaultController(ILogger logger, + IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + [HttpGet] + public IActionResult Get() + { + // var fileProvider = this.HttpContext.RequestServices.GetService(); + var fileProvider = this._fileProvider; + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 8f09b0a1..39c66c8f 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -1,11 +1,12 @@ using System; -using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NET5.TestProject.Controllers; using NET5.TestProject.File; using Unity; using Unity.Microsoft.DependencyInjection; @@ -19,6 +20,7 @@ public class UnitTest1 public void Autofac注入ServiceName() { var hostBuilder = WebHost.CreateDefaultBuilder() + // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureServices(services => { services.AddAutofac(); }) .UseStartup() @@ -62,6 +64,39 @@ public void Unity注入ServiceName() Assert.AreEqual("ZipFileProvider", result); } + [TestMethod] + public void 手動註冊() + { + var hostBuilder = + WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(services => + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(p => + { + var fileProvider = p.GetService(); + var logger = p.GetService>(); + return new DefaultController(logger, fileProvider); + }); + services.AddControllers().AddControllersAsServices(); + }) + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "default"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + [TestMethod] public void 注入FuncName() { @@ -88,8 +123,6 @@ private static void ConfigureContainer(IUnityContainer container) container.RegisterType("file"); } - - private static void UseUnityController(IServiceCollection services) { services.AddControllers() From 8c4950c02d3cd93cc213af415b5586caeb6f180c Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 14:21:51 +0800 Subject: [PATCH 101/267] refactor --- .../NET5.TestProject/UnitTest1.cs | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 39c66c8f..d116d7ce 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -22,8 +22,12 @@ public void Autofac注入ServiceName() var hostBuilder = WebHost.CreateDefaultBuilder() // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureServices(services => { services.AddAutofac(); }) .UseStartup() + .ConfigureServices(services => + { + services.AddAutofac(); + services.AddControllers().AddControllersAsServices(); + }) ; using var server = new TestServer(hostBuilder) { @@ -43,17 +47,22 @@ public void Autofac注入ServiceName() public void Unity注入ServiceName() { var unityContainer = new UnityContainer(); - ConfigureContainer(unityContainer); - - using var server = - new TestServer(WebHost.CreateDefaultBuilder() - .UseStartup() - .UseUnityServiceProvider(unityContainer) - .ConfigureServices(UseUnityController) - ) - { - BaseAddress = new Uri("http://localhost:9527") - }; + unityContainer.RegisterType("zip"); + unityContainer.RegisterType("file"); + + var builder = WebHost.CreateDefaultBuilder() + .UseStartup() + .UseUnityServiceProvider(unityContainer) + .ConfigureServices(s => + { + s.AddControllers() + .AddControllersAsServices(); + }) + ; + using var server = new TestServer(builder) + { + BaseAddress = new Uri("http://localhost:9527") + }; var client = server.CreateClient(); var url = "unity"; @@ -70,17 +79,18 @@ public void 手動註冊() var hostBuilder = WebHost.CreateDefaultBuilder() .UseStartup() - .ConfigureServices(services => + .ConfigureServices(s => { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(p => - { - var fileProvider = p.GetService(); - var logger = p.GetService>(); - return new DefaultController(logger, fileProvider); - }); - services.AddControllers().AddControllersAsServices(); + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(p => + { + var fileProvider = p.GetService(); + var logger = + p.GetService>(); + return new DefaultController(logger, fileProvider); + }); + s.AddControllers().AddControllersAsServices(); }) ; using var server = new TestServer(hostBuilder) @@ -100,33 +110,39 @@ public void 手動註冊() [TestMethod] public void 注入FuncName() { - using var server = - new TestServer(WebHost.CreateDefaultBuilder() - .UseStartup() - ) - { - BaseAddress = new Uri("http://localhost:9527") - }; + var builder = WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(s => + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton>(p => + key => + { + switch (key) + { + case "zip": + return p.GetService(); + case "file": + return p.GetService(); + default: + throw new NotSupportedException(); + } + }); + }) + ; + using var server = new TestServer(builder) + { + BaseAddress = new Uri("http://localhost:9527") + }; var client = server.CreateClient(); - var url = "default/zip"; + var url = "func/zip"; var response = client.GetAsync(url).Result; response.EnsureSuccessStatusCode(); var result = response.Content.ReadAsStringAsync().Result; Assert.AreEqual("ZipFileProvider", result); } - - private static void ConfigureContainer(IUnityContainer container) - { - container.RegisterType("zip"); - container.RegisterType("file"); - } - - private static void UseUnityController(IServiceCollection services) - { - services.AddControllers() - .AddControllersAsServices(); - } } } \ No newline at end of file From a9cb5c64cde226975bcaec1f5408500a01983ca0 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 15:05:45 +0800 Subject: [PATCH 102/267] refactor --- .../NET5.TestProject/Controllers/FuncController.cs | 12 +++++++++--- .../NET5.TestProject/ServiceProviderExtension.cs | 1 - 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs index 31f21928..43728f63 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NET5.TestProject.File; @@ -8,11 +9,16 @@ namespace NET5.TestProject.Controllers [Route("[controller]")] public class FuncController : ControllerBase { + private readonly IFileProvider _fileProvider; private readonly ILogger _logger; - public FuncController(ILogger logger) + public FuncController(ILogger logger, + Func pool) { - this._logger = logger; + this._fileProvider = pool("zip"); + this._logger = logger; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); } [HttpGet] diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs index 5384bc0f..6944fea0 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs @@ -11,5 +11,4 @@ public static T GetService(this IServiceProvider provider, string name) return (T) pool(name); } } - } \ No newline at end of file From 3b090d0dbc4f3df80df5a6efe07a84db846f19ec Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 15:22:49 +0800 Subject: [PATCH 103/267] refactor --- .../Controllers/UnityController.cs | 12 +++--- .../Controllers/WeatherForecastController.cs | 38 ------------------- .../NET5.TestProject/UnitTest1.cs | 2 +- .../NET5.TestProject/WeatherForecast.cs | 15 -------- 4 files changed, 8 insertions(+), 59 deletions(-) delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs index 2a748fc6..fcc4ccef 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using NET5.TestProject.File; using Unity; +using Unity.Microsoft.DependencyInjection; namespace NET5.TestProject.Controllers { @@ -13,20 +14,21 @@ public class UnityController : ControllerBase private readonly ILogger _logger; - public UnityController(ILogger logger, - [Dependency("zip")] IFileProvider fileProvider) + public UnityController(ILogger logger, + [Dependency("zip")] IFileProvider fileProvider) { this._logger = logger; this._fileProvider = fileProvider; } [HttpGet] - public IActionResult Get() + [Route("{key}")] + public IActionResult Get(string key) { var serviceProvider = this.HttpContext.RequestServices; - var unityServiceProvider = (Unity.Microsoft.DependencyInjection.ServiceProvider) serviceProvider; + var unityServiceProvider = (ServiceProvider) serviceProvider; var unityContainer = (UnityContainer) unityServiceProvider; - var fileProvider = unityContainer.Resolve("zip"); + var fileProvider = unityContainer.Resolve(key); var result = fileProvider.Print(); return this.Ok(result); } diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs deleted file mode 100644 index 2dc89a7f..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - this._logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index d116d7ce..80f0060f 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -65,7 +65,7 @@ public void Unity注入ServiceName() }; var client = server.CreateClient(); - var url = "unity"; + var url = "unity/zip"; var response = client.GetAsync(url).Result; response.EnsureSuccessStatusCode(); diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs b/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs deleted file mode 100644 index 1eb10ce9..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace NET5.TestProject -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int) (this.TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} \ No newline at end of file From fe65354bc2ffe6b6df7928d40685b83e2b5ab5bf Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 15:41:06 +0800 Subject: [PATCH 104/267] refactor --- .../Controllers/AutofacController.cs | 25 +++++++++++-------- .../Controllers/UnityController.cs | 5 +++- .../NET5.TestProject/UnitTest1.cs | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs index 29da9a47..50bdf475 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs @@ -1,4 +1,7 @@ -using Autofac.Features.AttributeFilters; +using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Autofac.Features.AttributeFilters; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NET5.TestProject.File; @@ -13,23 +16,23 @@ public class AutofacController : ControllerBase private readonly ILogger _logger; - // public AutofacDefaultController(ILogger logger) - // { - // this._logger = logger; - // } - - public AutofacController(ILogger logger, - [KeyFilter("zip")] IFileProvider fileProvider) + public AutofacController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) { this._logger = logger; this._fileProvider = fileProvider; - this._fileProvider.Print(); + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); } [HttpGet] - public IActionResult Get() + [Route("{key}")] + public IActionResult Get(string key) { - return this.Ok(this._fileProvider.Print()); + var serviceProvider = this.HttpContext.RequestServices; + var autofacServiceProvider = (AutofacServiceProvider) serviceProvider; + var fileProvider = autofacServiceProvider.LifetimeScope.ResolveKeyed(key); + return this.Ok(fileProvider.Print()); } } } \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs index fcc4ccef..c94576a0 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NET5.TestProject.File; using Unity; @@ -19,6 +20,8 @@ public UnityController(ILogger logger, { this._logger = logger; this._fileProvider = fileProvider; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); } [HttpGet] diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 80f0060f..21275860 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -35,7 +35,7 @@ public void Autofac注入ServiceName() }; var client = server.CreateClient(); - var url = "autofac"; + var url = "autofac/zip"; var response = client.GetAsync(url).Result; response.EnsureSuccessStatusCode(); From a8b2f4fe1025d58ffa5025a3154f2259c64afb5d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 15:55:58 +0800 Subject: [PATCH 105/267] refactor --- DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs | 2 +- DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs index 46dd9d76..a1721c5a 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs @@ -23,7 +23,7 @@ public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterType().Keyed("file"); builder.RegisterType().Keyed("zip"); - builder.RegisterType().WithAttributeFiltering(); + builder.RegisterType().WithAttributeFiltering();//<-- add line } // This method gets called by the runtime. Use this method to add services to the container. diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 21275860..775aa2e3 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -22,11 +22,11 @@ public void Autofac注入ServiceName() var hostBuilder = WebHost.CreateDefaultBuilder() // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .UseStartup() + .UseStartup() //<-- add line .ConfigureServices(services => { services.AddAutofac(); - services.AddControllers().AddControllersAsServices(); + services.AddControllers().AddControllersAsServices();//<-- add line }) ; using var server = new TestServer(hostBuilder) @@ -48,15 +48,15 @@ public void Unity注入ServiceName() { var unityContainer = new UnityContainer(); unityContainer.RegisterType("zip"); - unityContainer.RegisterType("file"); + unityContainer.RegisterType("file"); //<-- add line var builder = WebHost.CreateDefaultBuilder() .UseStartup() - .UseUnityServiceProvider(unityContainer) + .UseUnityServiceProvider(unityContainer) //<-- add line .ConfigureServices(s => { s.AddControllers() - .AddControllersAsServices(); + .AddControllersAsServices(); //<-- add line }) ; using var server = new TestServer(builder) From ae91f87295669cf6aa1d7f9ef823f8a986a5be88 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 22:39:53 +0800 Subject: [PATCH 106/267] refactor --- .../Controllers/MultiController.cs | 56 +++++++++++++++++++ .../NET5.TestProject/UnitTest1.cs | 45 ++++++++++++++- 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs new file mode 100644 index 00000000..48942f3f --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class MultiController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public MultiController(ILogger logger, + // IEnumerable pool) + // { + // this._logger = logger; + // this._fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); + // var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + // Console.WriteLine(msg); + // } + // + // [HttpGet] + // public IActionResult Get() + // { + // var serviceProvider = this.HttpContext.RequestServices; + // var pool = serviceProvider.GetServices(); + // var fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); + // return this.Ok(fileProvider.Print()); + // } + + public MultiController(ILogger logger, + Dictionary pool) + { + this._logger = logger; + this._fileProvider = pool["zip"]; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); + } + + [HttpGet] + [Route("{key}")] + public IActionResult Get(string key) + { + var serviceProvider = this.HttpContext.RequestServices; + var pool = serviceProvider.GetService>(); + var fileProvider = pool[key]; + return this.Ok(fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 775aa2e3..37fc84ca 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; @@ -26,7 +27,8 @@ public void Autofac注入ServiceName() .ConfigureServices(services => { services.AddAutofac(); - services.AddControllers().AddControllersAsServices();//<-- add line + services.AddControllers() + .AddControllersAsServices(); //<-- add line }) ; using var server = new TestServer(hostBuilder) @@ -122,9 +124,11 @@ public void 注入FuncName() switch (key) { case "zip": - return p.GetService(); + return p + .GetService(); case "file": - return p.GetService(); + return p + .GetService(); default: throw new NotSupportedException(); } @@ -144,5 +148,40 @@ public void 注入FuncName() var result = response.Content.ReadAsStringAsync().Result; Assert.AreEqual("ZipFileProvider", result); } + + [TestMethod] + public void 注入相同的介面() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + .UseStartup() //<-- add line + .ConfigureServices(s => + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(p => + { + var pool = + new Dictionary + { + {"zip", p.GetService()}, + {"file", p.GetService()}}; + + return pool; + }); + }) + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "multi/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } } } \ No newline at end of file From d1f9081e37980208d394fce096bcf9b76e47d7b8 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Fri, 21 May 2021 23:07:23 +0800 Subject: [PATCH 107/267] refactor --- .../ServiceProviderExtension.cs | 23 ++++++++ .../NET5.TestProject/UnitTest1.cs | 53 ++++++++++++++----- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs index 6944fea0..2c381a2f 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Reflection; using NET5.TestProject.File; namespace NET5.TestProject @@ -10,5 +12,26 @@ public static T GetService(this IServiceProvider provider, string name) var pool = (Func) provider.GetService(typeof(Func)); return (T) pool(name); } + + public static List GetTypesAssignableFrom(this Assembly assembly) + { + return assembly.GetTypesAssignableFrom(typeof(T)); + } + + public static List GetTypesAssignableFrom(this Assembly assembly, Type compareType) + { + var results = new List(); + foreach (var type in assembly.DefinedTypes) + { + if (compareType.IsAssignableFrom(type) + && compareType != type + ) + { + results.Add(type); + } + } + + return results; + } } } \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs index 37fc84ca..6641e17a 100644 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; @@ -154,20 +155,11 @@ public void 注入相同的介面() { var hostBuilder = WebHost.CreateDefaultBuilder() .UseStartup() //<-- add line - .ConfigureServices(s => + .ConfigureServices(service => { - s.AddSingleton(); - s.AddSingleton(); - s.AddSingleton(p => - { - var pool = - new Dictionary - { - {"zip", p.GetService()}, - {"file", p.GetService()}}; - - return pool; - }); + ScanToDictionary(service); + + // AddToDictionary(service); }) ; using var server = new TestServer(hostBuilder) @@ -183,5 +175,40 @@ public void 注入相同的介面() var result = response.Content.ReadAsStringAsync().Result; Assert.AreEqual("ZipFileProvider", result); } + + private static void AddToDictionary(IServiceCollection s) + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(p => + { + var pool = + new Dictionary + { + {"zip", p.GetService()}, + {"file", p.GetService()} + }; + + return pool; + }); + } + + private static void ScanToDictionary(IServiceCollection services) + { + var assembly = Assembly.GetExecutingAssembly(); + assembly.GetTypesAssignableFrom() + .ForEach(t => { services.AddSingleton(t); }); + services.AddSingleton(p => + { + var pool = + new Dictionary + { + {"zip", p.GetService()}, + {"file", p.GetService()} + }; + + return pool; + }); + } } } \ No newline at end of file From eb4d0226d14f4aff8f6de4768ba889be28fec6fd Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 11:32:09 +0800 Subject: [PATCH 108/267] add project --- .../Lab.LineBot.SDK/ILineNotifyProvider.cs | 21 + .../Internals/MimeTypeMapping.cs | 827 ++++++++++++++++++ .../Lab.LineBot.SDK/Internals/Validation.cs | 21 + .../Lab.LineBot.SDK/Lab.LineBot.SDK.csproj | 11 + .../Lab.LineBot.SDK/LineNotifyProvider.cs | 262 ++++++ .../LineNotifyProviderException.cs | 34 + .../Models/AuthorizeCodeUrlRequest.cs | 16 + .../Lab.LineBot.SDK/Models/GenericResponse.cs | 13 + .../Models/NotifyWithImageRequest.cs | 17 + .../Models/NotifyWithStickerRequest.cs | 18 + .../Models/TokenInfoResponse.cs | 26 + .../Lab.LineBot.SDK/Models/TokenRequest.cs | 19 + .../Lab.LineBot.SDK/Models/TokenResponse.cs | 10 + .../Controllers/AuthorizeController.cs | 61 ++ .../Lab.LineNotify.Service.csproj | 17 + .../Lab.LineNotify.Service/Program.cs | 19 + .../Properties/launchSettings.json | 31 + .../ReviceAuthorizeCodeRequest.cs | 20 + .../Lab.LineNotify.Service/Startup.cs | 52 ++ .../Lab.LineNotify.Service/WeatherForecast.cs | 15 + .../appsettings.Development.json | 9 + .../Lab.LineNotify.Service/appsettings.json | 16 + Line/Lab.LineNotify/Lab.LineNotify.sln | 22 + 23 files changed, 1557 insertions(+) create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/ILineNotifyProvider.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/MimeTypeMapping.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/Validation.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Lab.LineBot.SDK.csproj create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProviderException.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/AuthorizeCodeUrlRequest.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/GenericResponse.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithImageRequest.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithStickerRequest.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenInfoResponse.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenRequest.cs create mode 100644 Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenResponse.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/Lab.LineNotify.Service.csproj create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/Program.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/Properties/launchSettings.json create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/ServiceModels/ReviceAuthorizeCodeRequest.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/Startup.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/WeatherForecast.cs create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.Development.json create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.json create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.sln diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/ILineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/ILineNotifyProvider.cs new file mode 100644 index 00000000..3cb72d37 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/ILineNotifyProvider.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; +using Lab.LineBot.SDK.Models; + +namespace Lab.LineBot.SDK +{ + public interface ILineNotifyProvider + { + string CreateAuthorizeCodeUrl(AuthorizeCodeUrlRequest request); + + Task GetAccessTokenAsync(TokenRequest request, CancellationToken cancelToken); + + Task GetAccessTokenInfoAsync(string accessToken, CancellationToken cancelToken); + + Task NotifyAsync(NotifyWithStickerRequest request, CancellationToken cancelToken); + + Task NotifyAsync(NotifyWithImageRequest request, CancellationToken cancelToken); + + Task RevokeAsync(string accessToken, CancellationToken cancelToken); + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/MimeTypeMapping.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/MimeTypeMapping.cs new file mode 100644 index 00000000..f4dd853d --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/MimeTypeMapping.cs @@ -0,0 +1,827 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Lab.LineBot.SDK.Internals +{ + /// + /// Class MimeTypeMap. + /// + public static class MimeTypeMapping + { + private const string Dot = "."; + private const string QuestionMark = "?"; + private const string DefaultMimeType = "application/octet-stream"; + + private static readonly Lazy> _mappings = + new Lazy>(BuildMappings); + + /// + /// Gets the extension from the provided MINE type. + /// + /// Type of the MIME. + /// if set to true, throws error if extension's not found. + /// The extension. + /// + /// + public static string GetExtension(string mimeType, bool throwErrorIfNotFound = true) + { + if (mimeType == null) + { + throw new ArgumentNullException(nameof(mimeType)); + } + + if (mimeType.StartsWith(Dot)) + { + throw new ArgumentException("Requested mime type is not valid: " + mimeType); + } + + if (_mappings.Value.TryGetValue(mimeType, out var extension)) + { + return extension; + } + + if (throwErrorIfNotFound) + { + throw new ArgumentException("Requested mime type is not registered: " + mimeType); + } + + return string.Empty; + } + + /// + /// Gets the type of the MIME from the provided string. + /// + /// The filename or extension. + /// The MIME type. + /// + public static string GetMimeType(string str) + { + return TryGetMimeType(str, out var result) ? result : DefaultMimeType; + } + + /// + /// Tries to get the type of the MIME from the provided string. + /// + /// The filename or extension. + /// The variable to store the MIME type. + /// The MIME type. + /// + public static bool TryGetMimeType(string str, out string mimeType) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + var indexQuestionMark = str.IndexOf(QuestionMark, StringComparison.Ordinal); + if (indexQuestionMark != -1) + { + str = str.Remove(indexQuestionMark); + } + + if (!str.StartsWith(Dot)) + { + var index = str.LastIndexOf(Dot); + if (index != -1 && str.Length > index + 1) + { + str = str.Substring(index + 1); + } + + str = Dot + str; + } + + return _mappings.Value.TryGetValue(str, out mimeType); + } + + private static IDictionary BuildMappings() + { + var mappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + #region Big freaking list of mime types + + // maps both ways, + // extension -> mime type + // and + // mime type -> extension + // + // any mime types on left side not pre-loaded on right side, are added automatically + // some mime types can map to multiple extensions, so to get a deterministic mapping, + // add those to the dictionary specifically + // + // combination of values from Windows 7 Registry and + // from C:\Windows\System32\inetsrv\config\applicationHost.config + // some added, including .7z and .dat + // + // Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml + // which lists mime types, but not extensions + // + {".323", "text/h323"}, + {".3g2", "video/3gpp2"}, + {".3gp", "video/3gpp"}, + {".3gp2", "video/3gpp2"}, + {".3gpp", "video/3gpp"}, + {".7z", "application/x-7z-compressed"}, + {".aa", "audio/audible"}, + {".AAC", "audio/aac"}, + {".aaf", "application/octet-stream"}, + {".aax", "audio/vnd.audible.aax"}, + {".ac3", "audio/ac3"}, + {".aca", "application/octet-stream"}, + {".accda", "application/msaccess.addin"}, + {".accdb", "application/msaccess"}, + {".accdc", "application/msaccess.cab"}, + {".accde", "application/msaccess"}, + {".accdr", "application/msaccess.runtime"}, + {".accdt", "application/msaccess"}, + {".accdw", "application/msaccess.webapplication"}, + {".accft", "application/msaccess.ftemplate"}, + {".acx", "application/internet-property-stream"}, + {".AddIn", "text/xml"}, + {".ade", "application/msaccess"}, + {".adobebridge", "application/x-bridge-url"}, + {".adp", "application/msaccess"}, + {".ADT", "audio/vnd.dlna.adts"}, + {".ADTS", "audio/aac"}, + {".afm", "application/octet-stream"}, + {".ai", "application/postscript"}, + {".aif", "audio/aiff"}, + {".aifc", "audio/aiff"}, + {".aiff", "audio/aiff"}, + {".air", "application/vnd.adobe.air-application-installer-package+zip"}, + {".amc", "application/mpeg"}, + {".anx", "application/annodex"}, + {".apk", "application/vnd.android.package-archive"}, + {".apng", "image/apng"}, + {".application", "application/x-ms-application"}, + {".art", "image/x-jg"}, + {".asa", "application/xml"}, + {".asax", "application/xml"}, + {".ascx", "application/xml"}, + {".asd", "application/octet-stream"}, + {".asf", "video/x-ms-asf"}, + {".ashx", "application/xml"}, + {".asi", "application/octet-stream"}, + {".asm", "text/plain"}, + {".asmx", "application/xml"}, + {".aspx", "application/xml"}, + {".asr", "video/x-ms-asf"}, + {".asx", "video/x-ms-asf"}, + {".atom", "application/atom+xml"}, + {".au", "audio/basic"}, + {".avci", "image/avci"}, + {".avcs", "image/avcs"}, + {".avi", "video/x-msvideo"}, + {".avif", "image/avif"}, + {".avifs", "image/avif-sequence"}, + {".axa", "audio/annodex"}, + {".axs", "application/olescript"}, + {".axv", "video/annodex"}, + {".bas", "text/plain"}, + {".bcpio", "application/x-bcpio"}, + {".bin", "application/octet-stream"}, + {".bmp", "image/bmp"}, + {".c", "text/plain"}, + {".cab", "application/octet-stream"}, + {".caf", "audio/x-caf"}, + {".calx", "application/vnd.ms-office.calx"}, + {".cat", "application/vnd.ms-pki.seccat"}, + {".cc", "text/plain"}, + {".cd", "text/plain"}, + {".cdda", "audio/aiff"}, + {".cdf", "application/x-cdf"}, + {".cer", "application/x-x509-ca-cert"}, + {".cfg", "text/plain"}, + {".chm", "application/octet-stream"}, + {".class", "application/x-java-applet"}, + {".clp", "application/x-msclip"}, + {".cmd", "text/plain"}, + {".cmx", "image/x-cmx"}, + {".cnf", "text/plain"}, + {".cod", "image/cis-cod"}, + {".config", "application/xml"}, + {".contact", "text/x-ms-contact"}, + {".coverage", "application/xml"}, + {".cpio", "application/x-cpio"}, + {".cpp", "text/plain"}, + {".crd", "application/x-mscardfile"}, + {".crl", "application/pkix-crl"}, + {".crt", "application/x-x509-ca-cert"}, + {".cs", "text/plain"}, + {".csdproj", "text/plain"}, + {".csh", "application/x-csh"}, + {".csproj", "text/plain"}, + {".css", "text/css"}, + {".csv", "text/csv"}, + {".cur", "application/octet-stream"}, + {".czx", "application/x-czx"}, + {".cxx", "text/plain"}, + {".dat", "application/octet-stream"}, + {".datasource", "application/xml"}, + {".dbproj", "text/plain"}, + {".dcr", "application/x-director"}, + {".def", "text/plain"}, + {".deploy", "application/octet-stream"}, + {".der", "application/x-x509-ca-cert"}, + {".dgml", "application/xml"}, + {".dib", "image/bmp"}, + {".dif", "video/x-dv"}, + {".dir", "application/x-director"}, + {".disco", "text/xml"}, + {".divx", "video/divx"}, + {".dll", "application/x-msdownload"}, + {".dll.config", "text/xml"}, + {".dlm", "text/dlm"}, + {".doc", "application/msword"}, + {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, + {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".dot", "application/msword"}, + {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, + {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, + {".dsp", "application/octet-stream"}, + {".dsw", "text/plain"}, + {".dtd", "text/xml"}, + {".dtsConfig", "text/xml"}, + {".dv", "video/x-dv"}, + {".dvi", "application/x-dvi"}, + {".dwf", "drawing/x-dwf"}, + {".dwg", "application/acad"}, + {".dwp", "application/octet-stream"}, + {".dxf", "application/x-dxf"}, + {".dxr", "application/x-director"}, + {".eml", "message/rfc822"}, + {".emf", "image/emf"}, + {".emz", "application/octet-stream"}, + {".eot", "application/vnd.ms-fontobject"}, + {".eps", "application/postscript"}, + {".es", "application/ecmascript"}, + {".etl", "application/etl"}, + {".etx", "text/x-setext"}, + {".evy", "application/envoy"}, + {".exe", "application/vnd.microsoft.portable-executable"}, + {".exe.config", "text/xml"}, + {".f4v", "video/mp4"}, + {".fdf", "application/vnd.fdf"}, + {".fif", "application/fractals"}, + {".filters", "application/xml"}, + {".fla", "application/octet-stream"}, + {".flac", "audio/flac"}, + {".flr", "x-world/x-vrml"}, + {".flv", "video/x-flv"}, + {".fsscript", "application/fsharp-script"}, + {".fsx", "application/fsharp-script"}, + {".generictest", "application/xml"}, + {".geojson", "application/geo+json"}, + {".gif", "image/gif"}, + {".gpx", "application/gpx+xml"}, + {".group", "text/x-ms-group"}, + {".gsm", "audio/x-gsm"}, + {".gtar", "application/x-gtar"}, + {".gz", "application/x-gzip"}, + {".h", "text/plain"}, + {".hdf", "application/x-hdf"}, + {".hdml", "text/x-hdml"}, + {".heic", "image/heic"}, + {".heics", "image/heic-sequence"}, + {".heif", "image/heif"}, + {".heifs", "image/heif-sequence"}, + {".hhc", "application/x-oleobject"}, + {".hhk", "application/octet-stream"}, + {".hhp", "application/octet-stream"}, + {".hlp", "application/winhlp"}, + {".hpp", "text/plain"}, + {".hqx", "application/mac-binhex40"}, + {".hta", "application/hta"}, + {".htc", "text/x-component"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".htt", "text/webviewhtml"}, + {".hxa", "application/xml"}, + {".hxc", "application/xml"}, + {".hxd", "application/octet-stream"}, + {".hxe", "application/xml"}, + {".hxf", "application/xml"}, + {".hxh", "application/octet-stream"}, + {".hxi", "application/octet-stream"}, + {".hxk", "application/xml"}, + {".hxq", "application/octet-stream"}, + {".hxr", "application/octet-stream"}, + {".hxs", "application/octet-stream"}, + {".hxt", "text/html"}, + {".hxv", "application/xml"}, + {".hxw", "application/octet-stream"}, + {".hxx", "text/plain"}, + {".i", "text/plain"}, + {".ical", "text/calendar"}, + {".icalendar", "text/calendar"}, + {".ico", "image/x-icon"}, + {".ics", "text/calendar"}, + {".idl", "text/plain"}, + {".ief", "image/ief"}, + {".ifb", "text/calendar"}, + {".iii", "application/x-iphone"}, + {".inc", "text/plain"}, + {".inf", "application/octet-stream"}, + {".ini", "text/plain"}, + {".inl", "text/plain"}, + {".ins", "application/x-internet-signup"}, + {".ipa", "application/x-itunes-ipa"}, + {".ipg", "application/x-itunes-ipg"}, + {".ipproj", "text/plain"}, + {".ipsw", "application/x-itunes-ipsw"}, + {".iqy", "text/x-ms-iqy"}, + {".isp", "application/x-internet-signup"}, + {".isma", "application/octet-stream"}, + {".ismv", "application/octet-stream"}, + {".ite", "application/x-itunes-ite"}, + {".itlp", "application/x-itunes-itlp"}, + {".itms", "application/x-itunes-itms"}, + {".itpc", "application/x-itunes-itpc"}, + {".IVF", "video/x-ivf"}, + {".jar", "application/java-archive"}, + {".java", "application/octet-stream"}, + {".jck", "application/liquidmotion"}, + {".jcz", "application/liquidmotion"}, + {".jfif", "image/pjpeg"}, + {".jnlp", "application/x-java-jnlp-file"}, + {".jpb", "application/octet-stream"}, + {".jpe", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".jpg", "image/jpeg"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + {".jsx", "text/jscript"}, + {".jsxbin", "text/plain"}, + {".latex", "application/x-latex"}, + {".library-ms", "application/windows-library+xml"}, + {".lit", "application/x-ms-reader"}, + {".loadtest", "application/xml"}, + {".lpk", "application/octet-stream"}, + {".lsf", "video/x-la-asf"}, + {".lst", "text/plain"}, + {".lsx", "video/x-la-asf"}, + {".lzh", "application/octet-stream"}, + {".m13", "application/x-msmediaview"}, + {".m14", "application/x-msmediaview"}, + {".m1v", "video/mpeg"}, + {".m2t", "video/vnd.dlna.mpeg-tts"}, + {".m2ts", "video/vnd.dlna.mpeg-tts"}, + {".m2v", "video/mpeg"}, + {".m3u", "audio/x-mpegurl"}, + {".m3u8", "audio/x-mpegurl"}, + {".m4a", "audio/m4a"}, + {".m4b", "audio/m4b"}, + {".m4p", "audio/m4p"}, + {".m4r", "audio/x-m4r"}, + {".m4v", "video/x-m4v"}, + {".mac", "image/x-macpaint"}, + {".mak", "text/plain"}, + {".man", "application/x-troff-man"}, + {".manifest", "application/x-ms-manifest"}, + {".map", "text/plain"}, + {".master", "application/xml"}, + {".mbox", "application/mbox"}, + {".mda", "application/msaccess"}, + {".mdb", "application/x-msaccess"}, + {".mde", "application/msaccess"}, + {".mdp", "application/octet-stream"}, + {".me", "application/x-troff-me"}, + {".mfp", "application/x-shockwave-flash"}, + {".mht", "message/rfc822"}, + {".mhtml", "message/rfc822"}, + {".mid", "audio/mid"}, + {".midi", "audio/mid"}, + {".mix", "application/octet-stream"}, + {".mk", "text/plain"}, + {".mk3d", "video/x-matroska-3d"}, + {".mka", "audio/x-matroska"}, + {".mkv", "video/x-matroska"}, + {".mmf", "application/x-smaf"}, + {".mno", "text/xml"}, + {".mny", "application/x-msmoney"}, + {".mod", "video/mpeg"}, + {".mov", "video/quicktime"}, + {".movie", "video/x-sgi-movie"}, + {".mp2", "video/mpeg"}, + {".mp2v", "video/mpeg"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mp4v", "video/mp4"}, + {".mpa", "video/mpeg"}, + {".mpe", "video/mpeg"}, + {".mpeg", "video/mpeg"}, + {".mpf", "application/vnd.ms-mediapackage"}, + {".mpg", "video/mpeg"}, + {".mpp", "application/vnd.ms-project"}, + {".mpv2", "video/mpeg"}, + {".mqv", "video/quicktime"}, + {".ms", "application/x-troff-ms"}, + {".msg", "application/vnd.ms-outlook"}, + {".msi", "application/octet-stream"}, + {".mso", "application/octet-stream"}, + {".mts", "video/vnd.dlna.mpeg-tts"}, + {".mtx", "application/xml"}, + {".mvb", "application/x-msmediaview"}, + {".mvc", "application/x-miva-compiled"}, + {".mxf", "application/mxf"}, + {".mxp", "application/x-mmxp"}, + {".nc", "application/x-netcdf"}, + {".nsc", "video/x-ms-asf"}, + {".nws", "message/rfc822"}, + {".ocx", "application/octet-stream"}, + {".oda", "application/oda"}, + {".odb", "application/vnd.oasis.opendocument.database"}, + {".odc", "application/vnd.oasis.opendocument.chart"}, + {".odf", "application/vnd.oasis.opendocument.formula"}, + {".odg", "application/vnd.oasis.opendocument.graphics"}, + {".odh", "text/plain"}, + {".odi", "application/vnd.oasis.opendocument.image"}, + {".odl", "text/plain"}, + {".odm", "application/vnd.oasis.opendocument.text-master"}, + {".odp", "application/vnd.oasis.opendocument.presentation"}, + {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {".odt", "application/vnd.oasis.opendocument.text"}, + {".oga", "audio/ogg"}, + {".ogg", "audio/ogg"}, + {".ogv", "video/ogg"}, + {".ogx", "application/ogg"}, + {".one", "application/onenote"}, + {".onea", "application/onenote"}, + {".onepkg", "application/onenote"}, + {".onetmp", "application/onenote"}, + {".onetoc", "application/onenote"}, + {".onetoc2", "application/onenote"}, + {".opus", "audio/ogg"}, + {".orderedtest", "application/xml"}, + {".osdx", "application/opensearchdescription+xml"}, + {".otf", "application/font-sfnt"}, + {".otg", "application/vnd.oasis.opendocument.graphics-template"}, + {".oth", "application/vnd.oasis.opendocument.text-web"}, + {".otp", "application/vnd.oasis.opendocument.presentation-template"}, + {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, + {".ott", "application/vnd.oasis.opendocument.text-template"}, + {".oxps", "application/oxps"}, + {".oxt", "application/vnd.openofficeorg.extension"}, + {".p10", "application/pkcs10"}, + {".p12", "application/x-pkcs12"}, + {".p7b", "application/x-pkcs7-certificates"}, + {".p7c", "application/pkcs7-mime"}, + {".p7m", "application/pkcs7-mime"}, + {".p7r", "application/x-pkcs7-certreqresp"}, + {".p7s", "application/pkcs7-signature"}, + {".pbm", "image/x-portable-bitmap"}, + {".pcast", "application/x-podcast"}, + {".pct", "image/pict"}, + {".pcx", "application/octet-stream"}, + {".pcz", "application/octet-stream"}, + {".pdf", "application/pdf"}, + {".pfb", "application/octet-stream"}, + {".pfm", "application/octet-stream"}, + {".pfx", "application/x-pkcs12"}, + {".pgm", "image/x-portable-graymap"}, + {".pic", "image/pict"}, + {".pict", "image/pict"}, + {".pkgdef", "text/plain"}, + {".pkgundef", "text/plain"}, + {".pko", "application/vnd.ms-pki.pko"}, + {".pls", "audio/scpls"}, + {".pma", "application/x-perfmon"}, + {".pmc", "application/x-perfmon"}, + {".pml", "application/x-perfmon"}, + {".pmr", "application/x-perfmon"}, + {".pmw", "application/x-perfmon"}, + {".png", "image/png"}, + {".pnm", "image/x-portable-anymap"}, + {".pnt", "image/x-macpaint"}, + {".pntg", "image/x-macpaint"}, + {".pnz", "image/png"}, + {".pot", "application/vnd.ms-powerpoint"}, + {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, + {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, + {".ppa", "application/vnd.ms-powerpoint"}, + {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, + {".ppm", "image/x-portable-pixmap"}, + {".pps", "application/vnd.ms-powerpoint"}, + {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, + {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, + {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".prf", "application/pics-rules"}, + {".prm", "application/octet-stream"}, + {".prx", "application/octet-stream"}, + {".ps", "application/postscript"}, + {".psc1", "application/PowerShell"}, + {".psd", "application/octet-stream"}, + {".psess", "application/xml"}, + {".psm", "application/octet-stream"}, + {".psp", "application/octet-stream"}, + {".pst", "application/vnd.ms-outlook"}, + {".pub", "application/x-mspublisher"}, + {".pwz", "application/vnd.ms-powerpoint"}, + {".qht", "text/x-html-insertion"}, + {".qhtm", "text/x-html-insertion"}, + {".qt", "video/quicktime"}, + {".qti", "image/x-quicktime"}, + {".qtif", "image/x-quicktime"}, + {".qtl", "application/x-quicktimeplayer"}, + {".qxd", "application/octet-stream"}, + {".ra", "audio/x-pn-realaudio"}, + {".ram", "audio/x-pn-realaudio"}, + {".rar", "application/x-rar-compressed"}, + {".ras", "image/x-cmu-raster"}, + {".rat", "application/rat-file"}, + {".rc", "text/plain"}, + {".rc2", "text/plain"}, + {".rct", "text/plain"}, + {".rdlc", "application/xml"}, + {".reg", "text/plain"}, + {".resx", "application/xml"}, + {".rf", "image/vnd.rn-realflash"}, + {".rgb", "image/x-rgb"}, + {".rgs", "text/plain"}, + {".rm", "application/vnd.rn-realmedia"}, + {".rmi", "audio/mid"}, + {".rmp", "application/vnd.rn-rn_music_package"}, + {".rmvb", "application/vnd.rn-realmedia-vbr"}, + {".roff", "application/x-troff"}, + {".rpm", "audio/x-pn-realaudio-plugin"}, + {".rqy", "text/x-ms-rqy"}, + {".rtf", "application/rtf"}, + {".rtx", "text/richtext"}, + {".rvt", "application/octet-stream"}, + {".ruleset", "application/xml"}, + {".s", "text/plain"}, + {".safariextz", "application/x-safari-safariextz"}, + {".scd", "application/x-msschedule"}, + {".scr", "text/plain"}, + {".sct", "text/scriptlet"}, + {".sd2", "audio/x-sd2"}, + {".sdp", "application/sdp"}, + {".sea", "application/octet-stream"}, + {".searchConnector-ms", "application/windows-search-connector+xml"}, + {".setpay", "application/set-payment-initiation"}, + {".setreg", "application/set-registration-initiation"}, + {".settings", "application/xml"}, + {".sgimb", "application/x-sgimb"}, + {".sgml", "text/sgml"}, + {".sh", "application/x-sh"}, + {".shar", "application/x-shar"}, + {".shtml", "text/html"}, + {".sit", "application/x-stuffit"}, + {".sitemap", "application/xml"}, + {".skin", "application/xml"}, + {".skp", "application/x-koan"}, + {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, + {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, + {".slk", "application/vnd.ms-excel"}, + {".sln", "text/plain"}, + {".slupkg-ms", "application/x-ms-license"}, + {".smd", "audio/x-smd"}, + {".smi", "application/octet-stream"}, + {".smx", "audio/x-smd"}, + {".smz", "audio/x-smd"}, + {".snd", "audio/basic"}, + {".snippet", "application/xml"}, + {".snp", "application/octet-stream"}, + {".sql", "application/sql"}, + {".sol", "text/plain"}, + {".sor", "text/plain"}, + {".spc", "application/x-pkcs7-certificates"}, + {".spl", "application/futuresplash"}, + {".spx", "audio/ogg"}, + {".src", "application/x-wais-source"}, + {".srf", "text/plain"}, + {".SSISDeploymentManifest", "text/xml"}, + {".ssm", "application/streamingmedia"}, + {".sst", "application/vnd.ms-pki.certstore"}, + {".stl", "application/vnd.ms-pki.stl"}, + {".sv4cpio", "application/x-sv4cpio"}, + {".sv4crc", "application/x-sv4crc"}, + {".svc", "application/xml"}, + {".svg", "image/svg+xml"}, + {".swf", "application/x-shockwave-flash"}, + {".step", "application/step"}, + {".stp", "application/step"}, + {".t", "application/x-troff"}, + {".tar", "application/x-tar"}, + {".tcl", "application/x-tcl"}, + {".testrunconfig", "application/xml"}, + {".testsettings", "application/xml"}, + {".tex", "application/x-tex"}, + {".texi", "application/x-texinfo"}, + {".texinfo", "application/x-texinfo"}, + {".tgz", "application/x-compressed"}, + {".thmx", "application/vnd.ms-officetheme"}, + {".thn", "application/octet-stream"}, + {".tif", "image/tiff"}, + {".tiff", "image/tiff"}, + {".tlh", "text/plain"}, + {".tli", "text/plain"}, + {".toc", "application/octet-stream"}, + {".tr", "application/x-troff"}, + {".trm", "application/x-msterminal"}, + {".trx", "application/xml"}, + {".ts", "video/vnd.dlna.mpeg-tts"}, + {".tsv", "text/tab-separated-values"}, + {".ttf", "application/font-sfnt"}, + {".tts", "video/vnd.dlna.mpeg-tts"}, + {".txt", "text/plain"}, + {".u32", "application/octet-stream"}, + {".uls", "text/iuls"}, + {".user", "text/plain"}, + {".ustar", "application/x-ustar"}, + {".vb", "text/plain"}, + {".vbdproj", "text/plain"}, + {".vbk", "video/mpeg"}, + {".vbproj", "text/plain"}, + {".vbs", "text/vbscript"}, + {".vcf", "text/x-vcard"}, + {".vcproj", "application/xml"}, + {".vcs", "text/plain"}, + {".vcxproj", "application/xml"}, + {".vddproj", "text/plain"}, + {".vdp", "text/plain"}, + {".vdproj", "text/plain"}, + {".vdx", "application/vnd.ms-visio.viewer"}, + {".vml", "text/xml"}, + {".vscontent", "application/xml"}, + {".vsct", "text/xml"}, + {".vsd", "application/vnd.visio"}, + {".vsi", "application/ms-vsi"}, + {".vsix", "application/vsix"}, + {".vsixlangpack", "text/xml"}, + {".vsixmanifest", "text/xml"}, + {".vsmdi", "application/xml"}, + {".vspscc", "text/plain"}, + {".vss", "application/vnd.visio"}, + {".vsscc", "text/plain"}, + {".vssettings", "text/xml"}, + {".vssscc", "text/plain"}, + {".vst", "application/vnd.visio"}, + {".vstemplate", "text/xml"}, + {".vsto", "application/x-ms-vsto"}, + {".vsw", "application/vnd.visio"}, + {".vsx", "application/vnd.visio"}, + {".vtt", "text/vtt"}, + {".vtx", "application/vnd.visio"}, + {".wasm", "application/wasm"}, + {".wav", "audio/wav"}, + {".wave", "audio/wav"}, + {".wax", "audio/x-ms-wax"}, + {".wbk", "application/msword"}, + {".wbmp", "image/vnd.wap.wbmp"}, + {".wcm", "application/vnd.ms-works"}, + {".wdb", "application/vnd.ms-works"}, + {".wdp", "image/vnd.ms-photo"}, + {".webarchive", "application/x-safari-webarchive"}, + {".webm", "video/webm"}, + {".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */ + {".webtest", "application/xml"}, + {".wiq", "application/xml"}, + {".wiz", "application/msword"}, + {".wks", "application/vnd.ms-works"}, + {".WLMP", "application/wlmoviemaker"}, + {".wlpginstall", "application/x-wlpg-detect"}, + {".wlpginstall3", "application/x-wlpg3-detect"}, + {".wm", "video/x-ms-wm"}, + {".wma", "audio/x-ms-wma"}, + {".wmd", "application/x-ms-wmd"}, + {".wmf", "application/x-msmetafile"}, + {".wml", "text/vnd.wap.wml"}, + {".wmlc", "application/vnd.wap.wmlc"}, + {".wmls", "text/vnd.wap.wmlscript"}, + {".wmlsc", "application/vnd.wap.wmlscriptc"}, + {".wmp", "video/x-ms-wmp"}, + {".wmv", "video/x-ms-wmv"}, + {".wmx", "video/x-ms-wmx"}, + {".wmz", "application/x-ms-wmz"}, + {".woff", "application/font-woff"}, + {".woff2", "application/font-woff2"}, + {".wpl", "application/vnd.ms-wpl"}, + {".wps", "application/vnd.ms-works"}, + {".wri", "application/x-mswrite"}, + {".wrl", "x-world/x-vrml"}, + {".wrz", "x-world/x-vrml"}, + {".wsc", "text/scriptlet"}, + {".wsdl", "text/xml"}, + {".wvx", "video/x-ms-wvx"}, + {".x", "application/directx"}, + {".xaf", "x-world/x-vrml"}, + {".xaml", "application/xaml+xml"}, + {".xap", "application/x-silverlight-app"}, + {".xbap", "application/x-ms-xbap"}, + {".xbm", "image/x-xbitmap"}, + {".xdr", "text/plain"}, + {".xht", "application/xhtml+xml"}, + {".xhtml", "application/xhtml+xml"}, + {".xla", "application/vnd.ms-excel"}, + {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, + {".xlc", "application/vnd.ms-excel"}, + {".xld", "application/vnd.ms-excel"}, + {".xlk", "application/vnd.ms-excel"}, + {".xll", "application/vnd.ms-excel"}, + {".xlm", "application/vnd.ms-excel"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, + {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, + {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xlt", "application/vnd.ms-excel"}, + {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, + {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, + {".xlw", "application/vnd.ms-excel"}, + {".xml", "text/xml"}, + {".xmp", "application/octet-stream"}, + {".xmta", "application/xml"}, + {".xof", "x-world/x-vrml"}, + {".XOML", "text/plain"}, + {".xpm", "image/x-xpixmap"}, + {".xps", "application/vnd.ms-xpsdocument"}, + {".xrm-ms", "text/xml"}, + {".xsc", "application/xml"}, + {".xsd", "text/xml"}, + {".xsf", "text/xml"}, + {".xsl", "text/xml"}, + {".xslt", "text/xml"}, + {".xsn", "application/octet-stream"}, + {".xss", "application/xml"}, + {".xspf", "application/xspf+xml"}, + {".xtp", "application/octet-stream"}, + {".xwd", "image/x-xwindowdump"}, + {".z", "application/x-compress"}, + {".zip", "application/zip"}, + + {"application/fsharp-script", ".fsx"}, + {"application/msaccess", ".adp"}, + {"application/msword", ".doc"}, + {"application/octet-stream", ".bin"}, + {"application/onenote", ".one"}, + {"application/postscript", ".eps"}, + {"application/step", ".step"}, + {"application/vnd.ms-excel", ".xls"}, + {"application/vnd.ms-powerpoint", ".ppt"}, + {"application/vnd.ms-works", ".wks"}, + {"application/vnd.visio", ".vsd"}, + {"application/x-director", ".dir"}, + {"application/x-msdos-program", ".exe"}, + {"application/x-shockwave-flash", ".swf"}, + {"application/x-x509-ca-cert", ".cer"}, + {"application/x-zip-compressed", ".zip"}, + {"application/xhtml+xml", ".xhtml"}, + { + "application/xml", ".xml" + }, // anomaly, .xml -> text/xml, but application/xml -> many things, but all are xml, so safest is .xml + {"audio/aac", ".AAC"}, + {"audio/aiff", ".aiff"}, + {"audio/basic", ".snd"}, + {"audio/mid", ".midi"}, + {"audio/mp4", ".m4a"}, // one way mapping only, mime -> ext + {"audio/wav", ".wav"}, + {"audio/x-m4a", ".m4a"}, + {"audio/x-mpegurl", ".m3u"}, + {"audio/x-pn-realaudio", ".ra"}, + {"audio/x-smd", ".smd"}, + {"image/bmp", ".bmp"}, + {"image/jpeg", ".jpg"}, + {"image/pict", ".pic"}, + {"image/png", ".png"}, // Defined in [RFC-2045], [RFC-2048] + { + "image/x-png", ".png" + }, // See https://www.w3.org/TR/PNG/#A-Media-type :"It is recommended that implementations also recognize the media type "image/x-png"." + {"image/tiff", ".tiff"}, + {"image/x-macpaint", ".mac"}, + {"image/x-quicktime", ".qti"}, + {"message/rfc822", ".eml"}, + {"text/calendar", ".ics"}, + {"text/html", ".html"}, + {"text/plain", ".txt"}, + {"text/scriptlet", ".wsc"}, + {"text/xml", ".xml"}, + {"video/3gpp", ".3gp"}, + {"video/3gpp2", ".3gp2"}, + {"video/mp4", ".mp4"}, + {"video/mpeg", ".mpg"}, + {"video/quicktime", ".mov"}, + {"video/vnd.dlna.mpeg-tts", ".m2t"}, + {"video/x-dv", ".dv"}, + {"video/x-la-asf", ".lsf"}, + {"video/x-ms-asf", ".asf"}, + {"x-world/x-vrml", ".xof"}, + + #endregion + }; + + var cache = mappings.ToList(); // need ToList() to avoid modifying while still enumerating + + foreach (var mapping in cache) + { + if (!mappings.ContainsKey(mapping.Value)) + { + mappings.Add(mapping.Value, mapping.Key); + } + } + + return mappings; + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/Validation.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/Validation.cs new file mode 100644 index 00000000..d1bf65bf --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Internals/Validation.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Lab.LineBot.SDK.Internals +{ + public class Validation + { + public static bool TryValidate(object contact, out List errors) + { + var context = new ValidationContext(contact, null, null); + errors = new List(); + return Validator.TryValidateObject(contact, context, errors, true); + } + + public static void Validate(object instance) + { + var context = new ValidationContext(instance, null, null); + Validator.ValidateObject(instance, context, true); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Lab.LineBot.SDK.csproj b/Line/Lab.LineNotify/Lab.LineBot.SDK/Lab.LineBot.SDK.csproj new file mode 100644 index 00000000..600f4841 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Lab.LineBot.SDK.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs new file mode 100644 index 00000000..0b59d0df --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Lab.LineBot.SDK.Internals; +using Lab.LineBot.SDK.Models; + +namespace Lab.LineBot.SDK +{ + public class LineNotifyProvider : ILineNotifyProvider + { + private static readonly Lazy s_oAuth2ClientLazy = + new Lazy(() => new HttpClient + { + BaseAddress = new Uri(OAuth2Endpoint) + }); + + private static readonly Lazy s_apiClientLazy = + new Lazy(() => new HttpClient + { + BaseAddress = new Uri(ApiEndpoint) + }); + + private static readonly string OAuth2Endpoint = "https://notify-bot.line.me/"; + private static readonly string ApiEndpoint = "https://notify-api.line.me/"; + + public bool IsThrowInternalError { get; set; } = false; + + public HttpClient OAuth2Client + { + get + { + if (this._oAuth2Client == null) + { + return s_oAuth2ClientLazy.Value; + } + + return this._oAuth2Client; + } + set => this._oAuth2Client = value; + } + + public HttpClient ApiClient + { + get + { + if (this._apiClient == null) + { + return s_apiClientLazy.Value; + } + + return this._apiClient; + } + set => this._apiClient = value; + } + + private HttpClient _apiClient; + + private HttpClient _oAuth2Client; + + public async Task NotifyAsync(NotifyWithImageRequest request, + CancellationToken cancelToken) + { + Validation.Validate(request); + var url = $"api/notify?message={request.Message}"; + using var formDataContent = new MultipartFormDataContent(); + + var imageName = Path.GetFileName(request.FilePath); + var mimeType = MimeTypeMapping.GetMimeType(imageName); + var imageContent = new ByteArrayContent(request.FileBytes); + imageContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType); + + formDataContent.Add(imageContent, "imageFile", imageName); + + var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) + { + Headers = {Authorization = new AuthenticationHeaderValue("Bearer", request.AccessToken)}, + Content = formDataContent + }; + + var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + if (this.IsThrowInternalError) + { + var error = await response.Content.ReadAsStringAsync(); + throw new LineNotifyProviderException(error); + } + } + + return await response.Content.ReadAsAsync(cancelToken); + } + + public async Task NotifyAsync(NotifyWithStickerRequest request, + CancellationToken cancelToken) + { + Validation.Validate(request); + + var url = "api/notify"; + var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) + { + Headers = {Authorization = new AuthenticationHeaderValue("Bearer", request.AccessToken)}, + Content = new FormUrlEncodedContent(new Dictionary + { + {"message", request.Message}, + {"stickerPackageId", request.StickerPackageId}, + {"stickerId", request.StickerId}, + }), + }; + + var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + if (this.IsThrowInternalError) + { + var error = await response.Content.ReadAsStringAsync(); + throw new LineNotifyProviderException(error); + } + } + + return await response.Content.ReadAsAsync(cancelToken); + } + + public async Task GetAccessTokenInfoAsync(string accessToken, + CancellationToken cancelToken) + { + if (string.IsNullOrWhiteSpace(accessToken)) + { + throw new ArgumentNullException(nameof(accessToken)); + } + + var url = "api/status"; + var httpRequest = new HttpRequestMessage(HttpMethod.Get, url) + { + Headers = {Authorization = new AuthenticationHeaderValue("Bearer", accessToken)}, + Content = new FormUrlEncodedContent(new Dictionary()), + }; + + var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + if (this.IsThrowInternalError) + { + var error = await response.Content.ReadAsStringAsync(); + throw new LineNotifyProviderException(error); + } + } + + var tokenInfo = await response.Content.ReadAsAsync(cancelToken); + tokenInfo.Limit = GetValue(response, "X-RateLimit-Limit"); + tokenInfo.ImageLimit = GetValue(response, "X-RateLimit-ImageLimit"); + tokenInfo.Remaining = GetValue(response, "X-RateLimit-Remaining"); + tokenInfo.ImageRemaining = GetValue(response, "X-RateLimit-ImageRemaining"); + tokenInfo.Reset = GetValue(response, "X-RateLimit-Reset"); + tokenInfo.ResetLocalTime = ToLocalTime(tokenInfo.Reset); + return tokenInfo; + } + + public string CreateAuthorizeCodeUrl(AuthorizeCodeUrlRequest request) + { + Validation.Validate(request); + + var url = "oauth/authorize"; + return $"{OAuth2Endpoint}" + + url + + "?response_type=code" + + "&scope=notify" + + "&response_mode=form_post" + + $"&client_id={request.ClientId}" + + $"&redirect_uri={request.CallbackUrl}" + + $"&state={request.State}" + ; + } + + public async Task GetAccessTokenAsync(TokenRequest request, + CancellationToken cancelToken) + { + Validation.Validate(request); + + var url = "oauth/token"; + + var content = new FormUrlEncodedContent(new Dictionary + { + {"grant_type", "authorization_code"}, + {"code", request.Code}, + {"redirect_uri", request.CallbackUrl}, + {"client_id", request.ClientId}, + {"client_secret", request.ClientSecret}, + }); + + var response = await this.OAuth2Client.PostAsync(url, content, cancelToken); + string result = null; + + if (response.StatusCode != HttpStatusCode.OK) + { + if (this.IsThrowInternalError) + { + var error = await response.Content.ReadAsStringAsync(); + throw new LineNotifyProviderException(error); + } + } + + return await response.Content.ReadAsAsync(cancelToken); + } + + public async Task RevokeAsync(string accessToken, CancellationToken cancelToken) + { + if (string.IsNullOrWhiteSpace(accessToken)) + { + throw new ArgumentNullException(nameof(accessToken)); + } + + var url = "api/revoke"; + var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) + { + Headers = {Authorization = new AuthenticationHeaderValue("Bearer", accessToken)}, + Content = new FormUrlEncodedContent(new Dictionary()), + }; + + var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + if (this.IsThrowInternalError) + { + var error = await response.Content.ReadAsStringAsync(); + throw new LineNotifyProviderException(error); + } + } + + return await response.Content.ReadAsAsync(cancelToken); + } + + private static T GetValue(HttpResponseMessage response, string key) + { + var result = default(T); + response.Headers.TryGetValues(key, out var values); + if (values == null) + { + return result; + } + + var content = values.FirstOrDefault(); + + return (T) Convert.ChangeType(content, typeof(T)); + } + + private static DateTime ToLocalTime(long source) + { + var timeOffset = DateTimeOffset.FromUnixTimeSeconds(source); + return timeOffset.DateTime.ToUniversalTime(); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProviderException.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProviderException.cs new file mode 100644 index 00000000..58a527dc --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProviderException.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.Serialization; + +namespace Lab.LineBot.SDK +{ + [Serializable] + public class LineNotifyProviderException : Exception + { + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // + + public LineNotifyProviderException() + { + } + + public LineNotifyProviderException(string message) : base(message) + { + } + + public LineNotifyProviderException(string message, Exception inner) : base(message, inner) + { + } + + protected LineNotifyProviderException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/AuthorizeCodeUrlRequest.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/AuthorizeCodeUrlRequest.cs new file mode 100644 index 00000000..1d9300db --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/AuthorizeCodeUrlRequest.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.LineBot.SDK.Models +{ + public class AuthorizeCodeUrlRequest + { + [Required] + public string CallbackUrl { get; set; } + + [Required] + public string ClientId { get; set; } + + [Required] + public string State { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/GenericResponse.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/GenericResponse.cs new file mode 100644 index 00000000..f39e70ab --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/GenericResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Lab.LineBot.SDK.Models +{ + public class GenericResponse + { + [JsonProperty("status")] + public int Status { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithImageRequest.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithImageRequest.cs new file mode 100644 index 00000000..1d55a823 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithImageRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.LineBot.SDK.Models +{ + public class NotifyWithImageRequest + { + [Required] + public string Message { get; set; } + + [Required] + public string AccessToken { get; set; } + + public string FilePath { get; set; } + + public byte[] FileBytes { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithStickerRequest.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithStickerRequest.cs new file mode 100644 index 00000000..b0da5d02 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/NotifyWithStickerRequest.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.LineBot.SDK.Models +{ + public class NotifyWithStickerRequest + { + [Required] + public string Message { get; set; } + + [Required] + public string AccessToken { get; set; } + + //https://developers.line.biz/en/docs/messaging-api/sticker-list/#sticker-definitions + public string StickerPackageId { get; set; } + + public string StickerId { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenInfoResponse.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenInfoResponse.cs new file mode 100644 index 00000000..8f6d9af9 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenInfoResponse.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; + +namespace Lab.LineBot.SDK.Models +{ + public class TokenInfoResponse : GenericResponse + { + [JsonProperty("targetType")] + public string TargetType { get; set; } + + [JsonProperty("target")] + public string Target { get; set; } + + public int Limit { get; set; } + + public int ImageLimit { get; set; } + + public int Remaining { get; set; } + + public int ImageRemaining { get; set; } + + public int Reset { get; set; } + + public DateTime ResetLocalTime { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenRequest.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenRequest.cs new file mode 100644 index 00000000..07288524 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenRequest.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.LineBot.SDK.Models +{ + public class TokenRequest + { + [Required] + public string Code { get; set; } + + [Required] + public string ClientId { get; set; } + + [Required] + public string ClientSecret { get; set; } + + [Required] + public string CallbackUrl { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenResponse.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenResponse.cs new file mode 100644 index 00000000..8d72d9dd --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/Models/TokenResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Lab.LineBot.SDK.Models +{ + public class TokenResponse : GenericResponse + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs new file mode 100644 index 00000000..39a3c996 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs @@ -0,0 +1,61 @@ +using System.Threading; +using System.Threading.Tasks; +using Lab.LineBot.SDK; +using Lab.LineBot.SDK.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Lab.LineNotify.Service.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AuthorizeCodeController : ControllerBase + { + private readonly IConfiguration _config; + private readonly ILineNotifyProvider _lineNotifyProvider; + private readonly ILogger _logger; + + public AuthorizeCodeController(ILogger logger, + IConfiguration config, + ILineNotifyProvider lineNotifyProvider) + { + this._logger = logger; + this._config = config; + this._lineNotifyProvider = lineNotifyProvider; + } + + [HttpPost] + public async Task Post([FromForm] IFormCollection data, CancellationToken cancelToken) + { + if (data.TryGetValue("code", out var code) == false) + { + this.ModelState.AddModelError("code 欄位", "必填"); + return this.BadRequest(this.ModelState); + } + + if (data.TryGetValue("state", out var state) == false) + { + this.ModelState.AddModelError("state 欄位", "必填"); + return this.BadRequest(this.ModelState); + } + + var config = this._config; + var lineNotifyProvider = this._lineNotifyProvider; + + var lineConfig = config.GetSection("LineNotify"); + var request = new TokenRequest + { + Code = code, + ClientId = lineConfig.GetValue("clientId"), + ClientSecret = lineConfig.GetValue("clientSecret"), + CallbackUrl = lineConfig.GetValue("redirectUri"), + }; + var accessToken = await lineNotifyProvider.GetAccessTokenAsync(request, cancelToken); + + //TODO:不應該回傳 Access Token + return this.Ok(accessToken); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Lab.LineNotify.Service.csproj b/Line/Lab.LineNotify/Lab.LineNotify.Service/Lab.LineNotify.Service.csproj new file mode 100644 index 00000000..7cd965d3 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Lab.LineNotify.Service.csproj @@ -0,0 +1,17 @@ + + + + net5.0 + + + + + + + + + + + + + diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Program.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/Program.cs new file mode 100644 index 00000000..af088d45 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Program.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Lab.LineNotify.Service +{ + public class Program + { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } + + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Properties/launchSettings.json b/Line/Lab.LineNotify/Lab.LineNotify.Service/Properties/launchSettings.json new file mode 100644 index 00000000..39776679 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:24864", + "sslPort": 44336 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Lab.LineNotify.Service": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/ServiceModels/ReviceAuthorizeCodeRequest.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/ServiceModels/ReviceAuthorizeCodeRequest.cs new file mode 100644 index 00000000..ff163342 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/ServiceModels/ReviceAuthorizeCodeRequest.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Lab.LineNotify.Service.ServiceModels +{ + public class ReceiveAuthorizeCodeRequest + { + + [JsonProperty("code")] + public string Code { get; set; } + + [JsonProperty("state")] + public int State { get; set; } + + [JsonProperty("error")] + public string Error { get; set; } + + [JsonProperty("error_description")] + public string ErrorDescription { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Startup.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/Startup.cs new file mode 100644 index 00000000..1767dc7b --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Startup.cs @@ -0,0 +1,52 @@ +using Lab.LineBot.SDK; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +namespace Lab.LineNotify.Service +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Lab.LineNotify.Service v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers().AddNewtonsoftJson(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", + new OpenApiInfo {Title = "Lab.LineNotify.Service", Version = "v1"}); + }); + services.AddSingleton(); + services.AddSingleton(p => p.GetService()); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/WeatherForecast.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/WeatherForecast.cs new file mode 100644 index 00000000..1a6b47f7 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace Lab.LineNotify.Service +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (this.TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.Development.json b/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.json b/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.json new file mode 100644 index 00000000..5f313c19 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "LineNotify": { + "clientId": "Ppu33o7F0c2BTcryJ3PVDQ", + "clientSecret": "cf9ya2A9HA1TzWeXr7GF0ixqCC6vYtIb0Yq8KkOMSwj", + "redirectUri": "https://localhost:5001/AuthorizeCode", + "state": "NO_STATE" + } +} diff --git a/Line/Lab.LineNotify/Lab.LineNotify.sln b/Line/Lab.LineNotify/Lab.LineNotify.sln new file mode 100644 index 00000000..114055d7 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.LineNotify.Service", "Lab.LineNotify.Service\Lab.LineNotify.Service.csproj", "{AFD89464-B981-4C9D-8336-5E2A9A8A0F60}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.LineBot.SDK", "Lab.LineBot.SDK\Lab.LineBot.SDK.csproj", "{0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AFD89464-B981-4C9D-8336-5E2A9A8A0F60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFD89464-B981-4C9D-8336-5E2A9A8A0F60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFD89464-B981-4C9D-8336-5E2A9A8A0F60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFD89464-B981-4C9D-8336-5E2A9A8A0F60}.Release|Any CPU.Build.0 = Release|Any CPU + {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From b038c53ec2f782d4d3cf9f279e1091998e6ea8e3 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 14:34:55 +0800 Subject: [PATCH 109/267] refactor: use sockethandler --- .../Lab.LineBot.SDK/LineNotifyProvider.cs | 90 ++++++++++++++----- .../Controllers/AuthorizeController.cs | 2 +- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs index 0b59d0df..656bfe7d 100644 --- a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs @@ -14,30 +14,67 @@ namespace Lab.LineBot.SDK { public class LineNotifyProvider : ILineNotifyProvider { - private static readonly Lazy s_oAuth2ClientLazy = - new Lazy(() => new HttpClient + private static readonly string OAuth2Endpoint = "https://notify-bot.line.me/"; + private static readonly string ApiEndpoint = "https://notify-api.line.me/"; + + private static readonly Lazy s_oauthSocketsHandlerLazy = + new(() => + new SocketsHttpHandler + { + PooledConnectionLifetime = TimeSpan.FromMinutes(10), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5), + MaxConnectionsPerServer = 10 + }); + + private static readonly Lazy s_apiSocketsHandlerLazy = + new(() => + new SocketsHttpHandler + { + PooledConnectionLifetime = TimeSpan.FromMinutes(10), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5), + MaxConnectionsPerServer = 10 + }); + + internal SocketsHttpHandler OAuth2SocketsHandler + { + get { - BaseAddress = new Uri(OAuth2Endpoint) - }); + if (this._oAuth2SocketsHandler == null) + { + return s_oauthSocketsHandlerLazy.Value; + } + + return this._oAuth2SocketsHandler; + } + set => this._oAuth2SocketsHandler = value; + } - private static readonly Lazy s_apiClientLazy = - new Lazy(() => new HttpClient + internal SocketsHttpHandler ApiSocketsHandler + { + get { - BaseAddress = new Uri(ApiEndpoint) - }); + if (this._apiSocketsHandler == null) + { + return s_apiSocketsHandlerLazy.Value; + } - private static readonly string OAuth2Endpoint = "https://notify-bot.line.me/"; - private static readonly string ApiEndpoint = "https://notify-api.line.me/"; + return this._apiSocketsHandler; + } + set => this._apiSocketsHandler = value; + } public bool IsThrowInternalError { get; set; } = false; - public HttpClient OAuth2Client + internal HttpClient OAuth2Client { get { if (this._oAuth2Client == null) { - return s_oAuth2ClientLazy.Value; + return new HttpClient(this.OAuth2SocketsHandler) + { + BaseAddress = new Uri(OAuth2Endpoint) + }; } return this._oAuth2Client; @@ -45,13 +82,16 @@ public HttpClient OAuth2Client set => this._oAuth2Client = value; } - public HttpClient ApiClient + internal HttpClient ApiClient { get { if (this._apiClient == null) { - return s_apiClientLazy.Value; + return new HttpClient(this.ApiSocketsHandler) + { + BaseAddress = new Uri(OAuth2Endpoint) + }; } return this._apiClient; @@ -59,9 +99,10 @@ public HttpClient ApiClient set => this._apiClient = value; } - private HttpClient _apiClient; - - private HttpClient _oAuth2Client; + private HttpClient _apiClient; + private SocketsHttpHandler _apiSocketsHandler; + private HttpClient _oAuth2Client; + private SocketsHttpHandler _oAuth2SocketsHandler; public async Task NotifyAsync(NotifyWithImageRequest request, CancellationToken cancelToken) @@ -83,7 +124,8 @@ public async Task NotifyAsync(NotifyWithImageRequest request, Content = formDataContent }; - var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -114,7 +156,8 @@ public async Task NotifyAsync(NotifyWithStickerRequest request, }), }; - var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -143,7 +186,8 @@ public async Task GetAccessTokenInfoAsync(string a Content = new FormUrlEncodedContent(new Dictionary()), }; - var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -196,7 +240,8 @@ public async Task GetAccessTokenAsync(TokenRequest request, {"client_secret", request.ClientSecret}, }); - var response = await this.OAuth2Client.PostAsync(url, content, cancelToken); + var client = this.OAuth2Client; + var response = await client.PostAsync(url, content, cancelToken); string result = null; if (response.StatusCode != HttpStatusCode.OK) @@ -225,7 +270,8 @@ public async Task RevokeAsync(string accessToken, CancellationT Content = new FormUrlEncodedContent(new Dictionary()), }; - var response = await this.ApiClient.SendAsync(httpRequest, cancelToken); + var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs index 39a3c996..590b16e8 100644 --- a/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service/Controllers/AuthorizeController.cs @@ -54,7 +54,7 @@ public async Task Post([FromForm] IFormCollection data, Cancellat }; var accessToken = await lineNotifyProvider.GetAccessTokenAsync(request, cancelToken); - //TODO:不應該回傳 Access Token + //TODO:應該記錄在你的 DB 或是其它地方,不應該回傳 Access Token return this.Ok(accessToken); } } From 62210f0399e4addeb04202fc2f157110583961ab Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 14:37:14 +0800 Subject: [PATCH 110/267] refactor --- .../Lab.LineBot.SDK/LineNotifyProvider.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs index 656bfe7d..188da851 100644 --- a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs @@ -124,8 +124,8 @@ public async Task NotifyAsync(NotifyWithImageRequest request, Content = formDataContent }; - var client = this.ApiClient; - var response = await client.SendAsync(httpRequest, cancelToken); + using var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -156,8 +156,8 @@ public async Task NotifyAsync(NotifyWithStickerRequest request, }), }; - var client = this.ApiClient; - var response = await client.SendAsync(httpRequest, cancelToken); + using var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -186,8 +186,8 @@ public async Task GetAccessTokenInfoAsync(string a Content = new FormUrlEncodedContent(new Dictionary()), }; - var client = this.ApiClient; - var response = await client.SendAsync(httpRequest, cancelToken); + using var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { @@ -240,9 +240,9 @@ public async Task GetAccessTokenAsync(TokenRequest request, {"client_secret", request.ClientSecret}, }); - var client = this.OAuth2Client; - var response = await client.PostAsync(url, content, cancelToken); - string result = null; + using var client = this.OAuth2Client; + var response = await client.PostAsync(url, content, cancelToken); + string result = null; if (response.StatusCode != HttpStatusCode.OK) { @@ -270,8 +270,8 @@ public async Task RevokeAsync(string accessToken, CancellationT Content = new FormUrlEncodedContent(new Dictionary()), }; - var client = this.ApiClient; - var response = await client.SendAsync(httpRequest, cancelToken); + using var client = this.ApiClient; + var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) { From f6e34d7df45d1a6c7cae78dba7263d0995ee7377 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 14:49:16 +0800 Subject: [PATCH 111/267] refactor --- .../Lab.LineBot.SDK/LineNotifyProvider.cs | 93 +++++-------------- 1 file changed, 21 insertions(+), 72 deletions(-) diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs index 188da851..c23a2a6a 100644 --- a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs @@ -35,75 +35,8 @@ public class LineNotifyProvider : ILineNotifyProvider MaxConnectionsPerServer = 10 }); - internal SocketsHttpHandler OAuth2SocketsHandler - { - get - { - if (this._oAuth2SocketsHandler == null) - { - return s_oauthSocketsHandlerLazy.Value; - } - - return this._oAuth2SocketsHandler; - } - set => this._oAuth2SocketsHandler = value; - } - - internal SocketsHttpHandler ApiSocketsHandler - { - get - { - if (this._apiSocketsHandler == null) - { - return s_apiSocketsHandlerLazy.Value; - } - - return this._apiSocketsHandler; - } - set => this._apiSocketsHandler = value; - } - public bool IsThrowInternalError { get; set; } = false; - internal HttpClient OAuth2Client - { - get - { - if (this._oAuth2Client == null) - { - return new HttpClient(this.OAuth2SocketsHandler) - { - BaseAddress = new Uri(OAuth2Endpoint) - }; - } - - return this._oAuth2Client; - } - set => this._oAuth2Client = value; - } - - internal HttpClient ApiClient - { - get - { - if (this._apiClient == null) - { - return new HttpClient(this.ApiSocketsHandler) - { - BaseAddress = new Uri(OAuth2Endpoint) - }; - } - - return this._apiClient; - } - set => this._apiClient = value; - } - - private HttpClient _apiClient; - private SocketsHttpHandler _apiSocketsHandler; - private HttpClient _oAuth2Client; - private SocketsHttpHandler _oAuth2SocketsHandler; - public async Task NotifyAsync(NotifyWithImageRequest request, CancellationToken cancelToken) { @@ -124,7 +57,7 @@ public async Task NotifyAsync(NotifyWithImageRequest request, Content = formDataContent }; - using var client = this.ApiClient; + using var client = this.CreateApiClient(); var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) @@ -156,7 +89,7 @@ public async Task NotifyAsync(NotifyWithStickerRequest request, }), }; - using var client = this.ApiClient; + using var client = this.CreateApiClient(); var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) @@ -186,7 +119,7 @@ public async Task GetAccessTokenInfoAsync(string a Content = new FormUrlEncodedContent(new Dictionary()), }; - using var client = this.ApiClient; + using var client = this.CreateApiClient(); var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) @@ -240,7 +173,7 @@ public async Task GetAccessTokenAsync(TokenRequest request, {"client_secret", request.ClientSecret}, }); - using var client = this.OAuth2Client; + using var client = this.CreateOAuth2Client(); var response = await client.PostAsync(url, content, cancelToken); string result = null; @@ -270,7 +203,7 @@ public async Task RevokeAsync(string accessToken, CancellationT Content = new FormUrlEncodedContent(new Dictionary()), }; - using var client = this.ApiClient; + using var client = this.CreateApiClient(); var response = await client.SendAsync(httpRequest, cancelToken); if (response.StatusCode != HttpStatusCode.OK) @@ -285,6 +218,22 @@ public async Task RevokeAsync(string accessToken, CancellationT return await response.Content.ReadAsAsync(cancelToken); } + private HttpClient CreateApiClient() + { + return new(s_apiSocketsHandlerLazy.Value) + { + BaseAddress = new Uri(OAuth2Endpoint) + }; + } + + private HttpClient CreateOAuth2Client() + { + return new(s_oauthSocketsHandlerLazy.Value) + { + BaseAddress = new Uri(OAuth2Endpoint) + }; + } + private static T GetValue(HttpResponseMessage response, string key) { var result = default(T); From 8720295ca22921952e8db902cf3b28c254b9529d Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 18:44:18 +0800 Subject: [PATCH 112/267] add test case --- .../Lab.LineBot.SDK/LineNotifyProvider.cs | 13 ++++-- .../Lab.LineNotify.Service.TestProject/1.jpg | Bin 0 -> 150689 bytes .../Lab.LineNotify.Service.TestProject.csproj | 26 +++++++++++ .../LineNotifyProviderTests.cs | 42 ++++++++++++++++++ Line/Lab.LineNotify/Lab.LineNotify.sln | 6 +++ 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/1.jpg create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Lab.LineNotify.Service.TestProject.csproj create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs diff --git a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs index c23a2a6a..503010ff 100644 --- a/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs +++ b/Line/Lab.LineNotify/Lab.LineBot.SDK/LineNotifyProvider.cs @@ -64,7 +64,7 @@ public async Task NotifyAsync(NotifyWithImageRequest request, { if (this.IsThrowInternalError) { - var error = await response.Content.ReadAsStringAsync(); + var error = await response.Content.ReadAsStringAsync(cancelToken); throw new LineNotifyProviderException(error); } } @@ -94,11 +94,16 @@ public async Task NotifyAsync(NotifyWithStickerRequest request, if (response.StatusCode != HttpStatusCode.OK) { + var error = await response.Content.ReadAsStringAsync(cancelToken); if (this.IsThrowInternalError) { - var error = await response.Content.ReadAsStringAsync(); throw new LineNotifyProviderException(error); } + + return new GenericResponse + { + Message = error, + }; } return await response.Content.ReadAsAsync(cancelToken); @@ -126,7 +131,7 @@ public async Task GetAccessTokenInfoAsync(string a { if (this.IsThrowInternalError) { - var error = await response.Content.ReadAsStringAsync(); + var error = await response.Content.ReadAsStringAsync(cancelToken); throw new LineNotifyProviderException(error); } } @@ -222,7 +227,7 @@ private HttpClient CreateApiClient() { return new(s_apiSocketsHandlerLazy.Value) { - BaseAddress = new Uri(OAuth2Endpoint) + BaseAddress = new Uri(ApiEndpoint) }; } diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/1.jpg b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8852efa4039cb6a1c4b9800f02184453c8ad3db7 GIT binary patch literal 150689 zcmeFYcUV*Hw1EK*? zP*MO-z<e9bQIsQtK4FD806n{RzKOYK8Dr%Zjv~=_gj1&OX-^alJ z|33QP96S$DQczJ*Qd7~;P*a0q0>NVdH4_cFQjCcS-^oSl=KmtRm=R9snw!B*pHYU@6K`P$Oj_N~36 zx37O-aOmeSVPbM>dS-TReqnKaV{>bJhx~hYj|qfKNli^fO?v{Fg3<^4QZZ4}+_-&; zSyi9*q5B0c@xSO;)MBzee4^*RW3a~h$fJkhB9Fub@A?VqKcN5DFo*nq0{w58{|)l- zM}UcrVw7rtiULH>L_x(waZCVCQ&E6_P%#0@z&^80Bj^6yF`(WEK#5HlKpFNm=(Y;g zVc%9JtseEo=0(H9mLs)bUfp;=Y+Jc2n1-}{6cWs3vyDHR43g!iw3$5M2b7-#p4-X< zwmEI)ohXH(>y*c%j{yx40D8zz0nggI0$BG0uYUy60C!!Fmo`PNK&)8_skKqG8u zj{%?^Q&v56qP%$wST?UJZ$<_4Dr!!tP@(sZfx9YHLHY;tltLBTVnM%}sx#)>&%~0SP;VNg0T##4Lb3lIi z{W0LKOo};Tp*VxZXz=i!k9~c>{lj06IvjOYX)8+=6IPC+$ku(fELJb)2(PMOR5 zXnyNjKgE?1_1X77-~P<_tY||B=Gm8!Z<_Qvnm+~@fHN3bPA)Iy&x(zOfYgr=rF%<< zJ@bKul(H+?47Xk?c^xqwZtrB$Um=|TXABC9{|ZWM_+i^9Y=0WW$2D^IuMIxfYV=wr zU1s&mq7J_Cpb5@#S7#swl{Gi5+f3w~PywpL_)a#jJtG8Dpw7a+rO~cM2RQ?gW!alX zkMBPYnz&Z9LQ-L7ck)oWM>;ss2(01AZ+62Kf4np9v3T{&SEOPW3D{-azBY-s z&l{y|ijEqSWu0K?su~`Jb5y$0b%;KES;KqTUK-GpnJmW(Z8hRd>#9b)n~_Q>;$pZo zLLf((zhYfQzM9W{Oqv z>N7`|>c)l?gtA7T>1y(PTh}0Z|6Y@kg9ag`T6C7dvUrY7-Z+Jd&-ugr8FuT#3$E=V z4~yg=9GEn(P<~%!HC9M_`B)W`OnR+R^*jwD2;PBtMeK6QQ-~05 zKC$9k%!6lC)4CZaH}VU0SB41jn?ASqbFv<3x~lQBX>{xNR^-PL2I1zj!7;+_4Z7=Z z(3AHJ%;~O2+`1Yf_XBI$Gbra=J-9Q3db~3KX}=<2o?O*4qJ!GB@mqCzd7W+d!qM)v z45-w=Uo4zEw+N>eQayGMS=N;bu7Ba7NgGA+RVq6*cR9O!+4?G@T0Hyta~%_A4?-wTVGu1=E5*$^(nBcGy6u~btBR(u z6w^(e0tZYW`}OK|&M;Cw90~8FFYr)nxw=E*K2Lu`-|WM6N-e5PJ}ghg+R~8jVB^tB zAg!1u#Hp{UDbWXuT?!N#zx$BzU8{Po!$!Cc=tbZ(BH8+s7{d<3s4fSX2i&yo49t=Pr(jXpDhIE5%3a4!;!II9;qMG0pa7n z!B`6n{)qS&FmU@s#lPpUs9C~|3vXW1X&f<~%qxb-v**731BRv)^w2R70L~fI+D>jt z_x@J*|2poUDeM2marA;f&-#JmF`(iHB)mE8J{EjRg%$>zQ(>~~JX5M8{QW~VrS|QQ z!0A}X7LW{ll)mIwAT?ZBt;l14YEpGErbd7vz1ZH=L+)!TGcd5W9Ru8dF&%Zsjdbha zqT08dVPn%DdQ=*J|Ci{@m2OH`wj?Lkhi!v(hWc znU5BNmo}hzT!t6tEJs zc1`SvEBM@RO^N2+P3$r7@k#UVSAg>W9N)i_Ks7<+_jtSlaBh7D1x5zjS)7d)UyT~X z4-GsFjs7mmPjL*aMInMV=a9!h@OscX3kCbUg@TjN=8}i>iMTHg0Iw_jat@6J?^|I&HsW~!_N-&t^0A$EW4BP z|7qp^_s#QvL;_~!7%+8Z;oJXF5j*}&{X^KC3hl6~uq@`ysD|=PNBJ(Lkf0k=MpAj8HbH>^4l7a*)n>Dd~{C89!(T({^K7AEQRB{oeb z7wU&p#Tna?82zgFW1#VJfa&11o!1jMl1Nudy^fH_QiFE_zu2pdgfbK#zlKYDfKyJnDIZY!Qes-3H)$jbRShxL+&{Ajj<_}Ml7Ekzw?Vo|LS7SEU6xN>=g zw=^S3M`ZoZJF$s2#|~mj6(&K+WC~+wQGhAkQ)fGOs)j3hUF2!^%_3KHeO9_dEyO-D zw-PbAFQG=|QYbcvA)@l9ct{Pmq9VH3}kAL?_)M^dvepQuBvDJBlOB8YpmWZsCabHZ|Rv6ZQXz1k2deM~Yiy?ws) zQrB&sN;#Uj*_#65djs0!E-7-NfZBMcpxIM(mvF3MqKKH^f@t7bKasr4_{JO0J;tY^ zxB~VL^Rp*s9JcgbwesKPEf5lu3gDkJ2#kzo2$a{Wp))-Gc0%aHrSEUPy?hafHz*tTpOVebA#Kf0JsTS?5)<0bWw~paDx5J6MclcRmEeiaOX})_4O44;i(r|FX(UJAXij5H%L0EnKMsaoO@d(TVQYRaBP*nMCC=5SWM-x=taiAzmQY)tY^5ZxbMx~P*U}Z9NDDX`cQNWZ zKQldVgsNLNS22HxW@zE2g-#X~$+)P?*yS+AdBsYn&-?le4adz4u6T^~au!SD+yz0% z=i3EBo@H@^R~N-CH)8t|Nmetw(s{M19!V=o&dW!GM=hJ0V@E&sT~ns7)gJ?WNBG$( z`DX9g?Zei^Rv$z`WAVPc#rVctTuQ+qHGITtX7(6Jx>r_5=s(OO1?~E;$fC-}O^;6D z=4Qx^Sqb@sCA}cWuNE8q>Zk`?ks>a+1{yTIy0-PWs_@{;@izCSHB_r_s$#sX{1MU) zwS&op`(pMSu0pK`M?(|V1Cs1TVh)kcf%z3BrK`*17;k5Z6-H^V1uV(`QrWI;*G$C} zQBH`ce)doTnrPtec8xvsjDi5$zMc5H?6a#TXWlU5=hX5dzP5Fhr{;0&E*wbfVEX$5 zM>-5*pJ9x12g*uaw$N_+iQOswlCF;WQ0wjNEcUjv`obo2#dvrCtz(hdq6hoIXTD%u zlH&Ht*WO=km3G>N3Fz#HyzHTC^^-=CJg<1i1T^kMr`iqG&9L!y>?T(Rtj~}qo=C$I zq>+ZRJ{}P}Ch<>ub)=?C)A5q>YSw$9<;%NyraqP->!KC;IQU=eZTTe)6kj7LyWMxP zsIHIMo0+ZCZ?;a%JYCCkWao(|b*{`3EBxhH;;*HX#jp@anQ(6UEA@^8eCyE8MN`62 zZw?YC&Fvzv@O>(ftpkE6YMhq7EwcXMmC!`uC9d^SJpMIZXvI++#}`z>By&(6f04SU~S=fj)G_A_LOiN(K`PqsD?ZoWy`x zpDR(PEvEHqWyFR+9KV}jTBbg+G(A3)4ZzSw6H%sA_0(Ubo#w?buXU-X} z4U`TVj(uElTcdyEbmnVphuaSgJ9?g$)Pj_nl~Lo%m4*5nFJVN)VRBrAe~rhdR+6uO zZ?b`p%OL7!yA3hWN=7y)vLib&MSY{tT7U5LinT1jf2460`-8Z!RgIfPOk&m-g zd5b!eUhQ-`Kk)NT-}@sB@R)4@Osv6Dq7g;T)bH-2;4KQF>zAh*fz!cf(=)ojB#&Y; znB%=4STFsi5I^<%(k=s#pl6#0Fg)w4J@Nts&psg?(g61g6=tV?U)XI|VP0liAOurB zzUQgW8miGDrtD`OQkDE z!4A(9QW~pvbIb#D;!lnN85V$=O~Eh70tkQO+u64Er+&&$ancg>F^AQ61}RN z2B>=ks~rSUozzfSE-UB(=Rts6nI9*?IXWu6xht3}{M3TX(d}a(lNW%Vw_8^^^(j3q zY&(L#o4@H% z7eC|R2aK+zb#G=;=y|pU&GS?8s?JGse$42C0!g62et?DTs^5uG830Yrxl_ShH7YFb z2kytfJW_?Z5|Gmaz5xp8Oq!oy62YYRz?~4xmGIPQ?{E`LC*0>LO{~-W_L2cMH~F2_ zKqsnCN&@CTpJY!G*d&pGXXkY4G%I5fZ`krpjZ;g-wbCEFzGm>iQ~GTL+Kw$h2^X5> zC^IbMGV16rYD0obHDaOK)={pB5&c`wbT*~!FLAhxI~Dj8WBco_v9I%(1-V#_A@-hT z6-J6{T=^!ZAhq0;b@03VL48owF9AZN^97ntmmBVp0_qyZEGYG^TyAt0T)r;+&-EUtS+Lc_{XE<4Np@!#h!I;W6-NMC3bCDEBtTsUoD3 zKm1k69SvRbX;YyP-&+mcac8^=!i|O6;S*jNV#0ewfsOJ-fu=AMuJ(@X`X~+!#;Up3 zDTv1;N1;Z848b5~tEY{wlPiWTaz4*z2WQ^bCf!G@@>Q8^Sl>&YeqqMC(t6+dC9E^D zcDKZg*T8rbu3vD)rP~Sx6|b)26Oa|F>)5l`uQQq#;F&QgGb19L3Ts>UoO`3iy2$hP z2ql3b37fOOGp(I{r0*=4elTxCFU)Bb+BTYtJ~Oj4BQ4_HdlD|;K4 z$aD~zbS+RN3zd!4jRakV(VpJ(@wdH;`0(vv1qK51@yR7ZBi22z{BTU~On`N4(h(;@ zBiepSK=`y?CyFHqQF+coYyV!{E+-;9JLahu|ML_^cK3HMsLR=2lj*{Et|9SAN%Rzd zy;ad+{1hAC%~5z|YfrK+Y5bWtFUQsVBQ_0Fv}K*urldVG#!A~ie_Z;d*rk^03`A|B zj>(hd>Ap``1$eFp@8H1gtEcay9|jvpO{|59uw>ob6Y|cLmZaVI`31(!* zaJ36Zi5*|?3GaH|I?|ST)uC)1!wLs-m!Ot)8shnx@ej(JJ}s`e)R~3xZu=TXt@YTH z5j93g4yD-N2Ab)=7X^g9tz3u>gCvc2jV?^&XUJ~99zdA&f z;+CfFq`kxxRr~{Ld?nt12-SunT~J-M>U>c70u2vDfKI{9jNi69b%nW6ITO^CZIfkk zw-1Z1SUTlT8u#-E$KC%)RD3`7aYXu{(t)CB2iaE{v3?8M^9p)9sf}~Yus4vU@htrT z3tz>ZGJUD0Q#raO+WS4t%}eZ8Z$_7>d5_$xd%o`+IcfY#+awWI`C1I~P>bEovgv%q zTaE5r$FZ7?alb^41Jh6a37R4ng@x^{L*qnp%D|>Koc&RGUU*YtVZX?r+@=j)TfI6> zF6D|zx|l>e7Sf6>9Bs<+cj0>9eEVghVkDE(@YT>q-}C;Oa-z_E^7)d{tOc&%&mrm1vk`(wCoQ2kLI&g@KjY zHCZ+NQ`Iq_Bl9^GzyFoD0kMCX@I~B}n+B%tbGhy!IrVyfzje8&i-EfhqIErTySjQ7 z4HIOrS>fO|K*psQv#T)(G=+CC`-`HUvWzBi{90lwm(bqNIJbE5jn!gH*%*POe^)du zKWv0&ql$)?SY4vUM5!?mV?Ii4{OB+O(Oy8W9Lr)gTC+>jSY%<0I%^JMmx zF1ASWi#h7b6WhO}XAdiBd#a7_-pnQFC*7V%DN{m5Drl}b8@#w1_geyu$uCO8%iq%_F7aE;A zbQz4sYvOFax|&vZ){T0_UiXNIiAW*Lu=!WF~eLHT^KnFJhXcmgnp`7OAC1(rS^Gl$9s7cLw&N z5oCyfMx&ge_4GoQUw}@_Cp96QB4Kz$bNT=YS*H!(g8Qc|X`xk2bG(gSm)FakHg)mv0mn{en6W*A&#vNf zy@&)t(hFnY+>7qBimNj-^qDnv>2*F78zeqX!KN4GS8I_<-u~nVGAV-@fPeQx#jTHf z^3#S8;&Po+T9^BDG7rwADc!5Be&y6P9V=mfJzZ5#9k4K-YMylh zjQ6$`yUPHEM!)TCC9oJ_e7NboA3nO*u5w|81wdVk|KIv%QuGF245qxJ!c@FI-t4)g z!gQ}$BJJt#)u5Vq3Vv&t&G#P^xLMKp zO90}(d{##HegK`peqe8#1GluB41^2^d89p^B!i%N|7WT&uYj^aIjqy(q|tpnz?9ni zpgSv*uKdyr$J2wrZt`F-ul+|7DAmN0YN((nlggFj7`UTCSwU3+GgLW0QqkdexGSGQ z!UE8ep$Z#A72pIr*ruo4@B@>rdCHqM<6<^7hux)K6av%KD$VUL zff?v8A5f~0D8*7=Kj^loW8hsTh3Q`>_M)@5Nmhc5D`kv={n3P^0=^2|Z#^0jlevAR zE@V0UTlh3pe|93(IyVc&W=0zaSL>I%efepRq~F_^e~oME5uoYp#zvjx_$&6ld%BK} zm=D`?_nwYYqm@A>17ynnhj>g=cxOHL`PBDM@{okkT~ma10%EoohmM)Pc5CfBp(|&S z2o*vyzFg)xyJ^|(fjj?<9yM5UZq+JkG(&mjo27g5RBr99j-1}##p`vyE4EY23kt*< z64SR+>$&rFbn8O~fBJb;{YEXP7nS*9wtAK9ZNd`W?@m^nRd+q}O7{a>xzblxi@-{A zT8Nm^j}TV0>6xPl3xz6AoI_w}tvhRMkE-!&{bnAExo42hvEOAJJ`H|VQGN!IM|>t1 zo4@uZ`@xAb)s4#yZv)KM3c@hTOfAT~5i1*CF6~2mu7r1km%MGCg>VTz&vgM9)~AC# z6qe;8_KXW-!JP(|Wb|@r9)AJn)IdtS7uwx4&gAGYKW?BcK_aa; z&?d~~Ky0Go3gXfg<9;`%0BB$E?8km^p6nuvKusv zD4sc}PqhDjcaJm1k=Gu7TKHGS`eJRx%;DFZ3fIbtsyYN_V88p>rQ24-C&iP)gJyaC zxlg7epK+J?&X}7+Xtb_}oTZyK<%x^3Vp&bzInsH$8q?z~+^QonRP`BEJXRbu9kBoF z7&vdSNNXkb2c4 zEBwoOAxou%Y$CzEh=!5Rw@cx@4;m+&@f1ho5`t*Nlk(yM@Xg*lB%E%&jgGV;)y}gX z2~OOC-19Z{bpIuP|JFM)$`7r+??zfa)~}g0GJfjboadoGZT6fkfH@KZ3yRc8eOO(h zo^8_X;B7SP#ml>{(G!-l6Z;f4Mw<5}eHA(*DpTR=#qP?Smph2=Y4da`ty36W8~OUi zw|srK@^c~OheWji{OF*!WpezYxm<#+fpu@Yv|v$wY^6cKP+net9;aHIPK&0smaf1T z)15q?u{r^%0+jGOLI7*u29XMO&^YoP_6>6W~xwq(MywYVHeLi8qD;{o9yOGnq1zRzH zk>I6++u?0{J*Wk2D)qaZE)X8p=Zu>PIdyjBP}AZI)g|4_7T!wyzmSC^xyG_Z0zv*6 zNQ7;GPDSKQQ^JbSb`tO8lxtPTGFUdAelUU{=q5atJoMDj(T1fM^rIZ?k{5-ZzcYe7 zrcLr>(Gd4>BtLAuoTSV(#3APKoGy=K^jW|#P%+nGa(CO$qcE?d8a0fqxYw5!aJRW0 zb^%f1T+|^nv1x#+Ci!%-+KpI8I!-`J6&N4p-Fl7M)` zm72s0b=mXpPCv7EbFAWNvV5AP?d6FvR_rCgctOo#{gC*wx`85%cHgnVA=~?dbH5{} z@Zu&oq|GuTn`1+0?3nt%NB#$ngjz4+d1b-0_UnQSZ=^C~69Uu%dZr7YXDmiZ<^=rs zWEtbis!g%$6?~0;_CW5OV5DOI1>LiQ#0}LYq~Po7YK&@qtF`#UZx{7K0*^Xm4$sY4 zd)w?@oIG?sGK?I}TIj=L9y+ zB{$Y7lgwOfcK0<7nihwx7nVePsyE1qVZsv6!9Jkh5ILi*rwl{b*ChI7s3J`*nqLN( zmLg2*bkoYP#RQ%Ui{~wNEdo%058~=qzEt_!_IJ5i2_r@kWp3)$@r(7U`xXrY7W=n7 zpiGUm~iKA`HD|vzWS(^I$*Y zE=TQ?BNL`pkDeWdCpcc-V%X4nB`*lbd;}i| zYq^X*kga4)R)iH)fDI|@hV-T}kmYoe{dqi{^z&<9QMo8i>jy}mv-p2}^)#LTed77g zh!k{S>UP^Ss-XEqB3JlRgsgIa-yiydUPC%;0VD>X=8EPO6%Px6k#55udmuf4+Moi@ z$w$h7ttx|wRcBw^&^ieu!GXT`i6$Yj~;YsAq_WkIEB~I`x)0lSBiO%9vS3%Q;5rR)y`Y3AtF-@(_ zXfxlcbU_x#w0O1Nf0O|-&Hi@C$%ubDW~zv^u(b7eG6);LmN5!*KFJ2-PIPAXUGzJ5 zGN9CE|CUlZ$c11j+0<;`pX=L?26+{gThrS+C*%n>H@Wn&bdZtqjl-19`h zpS%z@Y6DXRPcQx(0cAz)V&DlwL~t9t{Dg(aTtP~SBVXtn1cy#?RfNG@*D`?$?zr(j zkYGkx!yUKh$LS2bns}<;zQ4W~6bM??#PV#xpMlt(DEzS;BQEOi9qQJ!d<7>dA;QF} z=b)Y3rF8eO?HF(g=CMzSeg_g&+>U|u4xn|NtMf2TY1Tk0SVc`nxRIPOqjI(2)e-yr zm}CR4anKg|mm^D$F#MfWjID(0Bt*JEbvuWixKhP$ycde437LbVb|ZPWEa7jtN)k( z8vKh7A))agacRe2-u7|gjq`9;A~(sNv7xnGK0QL<;ww!;?raIU0b>T>-uX2z;66m@0X9EvYSv>@0GJ)B4CaeO{L(=lJ4 zc>a3*1pVDywE}r7yXqDkl8t-mA}jN<<@RaVnEbBdb0=c2#e7+FiXYlyr5qRdI8OuL zDCyIu8;&F{G$osRag-fgkn8mM!TK6HrmU#mB}`^#4~a~ih>)_rK+

Iy%kR&p7S$A_r-!!9PoVOP4 zT8xm`lSqC~a+qPsK@)SbEr;r?bxGS260lH^&KYwV8|a7h_x@QOXY=f_?N3{#x3QPk zUKPyHb>gU8ZV5q@5#NjbN7v)+{i}B;CyC_Q!G$ff9dD=OIqz2e*y`j~sEsFb%*rsZ z1FcN8mBJV)XSVA46rXb|kC! z%oZ+6RJ-($ZM;{P0xi(4Nq2Lqf16%=ouwSM#lDZ1w5vfJ{b0u@7;ju*rOqyXEU&6} zR%0`Xr>`*0tS>a9o_dNmZ|;(Rky0{d%Y=+VO7UBy78o#&C8YYRmcu3LmdS&?RW9GM z_&zC$qnO*ixO)2Tu+yg_^zG^z&9;*4E$;=&no=leKYH6op>{Rdzt&=<9(Qx5b}7zGUD%^F^RtU8vw~*lo7j5k@C&D}o<;>sKX|Zi4pr#! zTJ0TjMB11PrELf9jrNKqLeV-Uu4TQ$%3=%G-~|C!dbi5X&Z$+WFUpGLnHMyhGNQX; zX>C$Z1E&lxe>%JD(WPVAINz7w__J?-e?GO=*N(6}7#*~bv=!F3dqsh$VB@$10Sl7ap^c%mG5v7ZsOBa))3Ua&sUM30Iy3g=@mY}O|?S*yR zQhn6;IYigD|8d@_k0%;E=GU*2FH{rAQmtnt@DvpSNuN1Zzd8@7B zFkWMABymKDh(BEoHUiJf?ki}c1lJ%j?h58HQRs}+0Y&3$s5GPvdI$%_%OhOTLr+Pg zF0niBA$)#rXg`gW{-N@6!a|R0pDZ@*QP;|>v;dFWm~-yEp3WC$!iicru3M>(4ZEbK z+(iLNeAk2yei(cAf|$^k%SHJItA|7WtqBYM_|0-HQQyy(5YVP8@=J&b+41(WA&ch5 zqY0$hkT+w_nS@pcaqWjPSL`Y+zO7q{4DHD#l;^Y?0uRN~C_4%cEl^*)p;xgik^xwx z>J}_9wBX{v;0M1BX+M4}dfCX{4_aI~S3obc*tgSfE9A75oFQzLkR0o(YirQEWZDN` z)(^@KELwHM9reXj*u5AV6LRa(S|`SK3wQGr>WnrST@N|O)OHqntG;T-YfhFoCw8YI z!Ftf-8PsqlNo0au+x!ZD{%Rc5pg;#1b?CERa+VBCn_TT9 z8yX-s4GSl0dE#vIN*4}9JX^&Pn;NTw=t^f%gV!PC9D|I;Zd%^Ud(mN$U(D6WqLo#u z^rM9xklW=OExB(#IJ934q4*^H)ONt!qrWgA)-WNqhc_GBXq2Jukb`XLu*9S-d?JVX z3=oOLraE&)NkpeZ4oAnw(SKI~{yPyzzTlp|&gll*5CT^S(U^ZA#}yq>Egb_^;5vdt ztKNrkr08GDqp+QMN|h?RgZ7c%=Z=A^nN*KQnl`*RKL#EH3wZ#$1Cbn;bwsst4CwGv z%Ki^iHa3U0G2NWs=PC}pGARZhbH0-c`a73H4>vbtkUt#*Q}GmMDm!Oxvfcg=6ufs1 z9Qob}tfSw`2OhyR=FJV@5)e+Cy#*D@($RzVq5ZHQL48ml(!@rx$*$|`5!K=`u=0aC z9x3QRiH&EG58EgXJ1c|IxmFIp9x&H&92d<+CO zk7UxbI?T=LHjkcQ1x^h1FMO2vxJ_{71?~7zvL5y0%H5L9n`q@VPNP3bt&o)F_q0DU zK>2JAQzsIX!oV|uPHA#n;lCUrpODlMP-$x$M6sm|g3WXIwEK@)fd7h{OZX?K>_4~# z@zMWgE~c-Qt8u3@0)i+&8aGabnI?sAR(Jg4iLQSfn0Z7+I&mN{B1LLQJGaK^x`$9< z>Zn|eo`p5JZ*ZS)qs6-oqTkVmr4`_d!49f*V`p3^;JUg6T1f6^g3_*c^ET{DB#6r%MyJet-_|`a0 z|5)e`CgqwFuzwu*ue%NSV>&P-o;wMNbLQxX%D}B7szWed{&`vWw@6B1eqybEZU~wY z5P8rz?THqYg808S!KFQ%N3tsPqANXHCoB0*xW_p6zB1s^?AdpjWsqnws zO8H;kNhPn_z4LtRHd&-O9z#)A|Dly1uo@%6K!%5pXJ5q@3kCD)W=g>*Y>pT3uocX*)g)N*0X^nimf88*U;^+1B_+$iEhoa$aiKSV1mnh@w zyEdjMUyNx5CgM`-E{n}m`|sK5dsfwnLxJBJQLdlIZbI}Joh;x_%5xKrzQ^8Z2%n&R z3P<(3N5)==eYKuHd*+olf|spThbQ&ds8y4UU|hWQXjSC&%uYNHu^)c(j($8ZyA;9Q zVt8LLz2gc3tJz6Y=Q6g0P{hk0F_lTlKM?`n6g@h5YfY-pcnX44glHFqg{qBr=P$N% zD@ErAri@H1CE+t_eXCb1gl-sFRAt_k>EUl$Q~QNAAVGj+?C{2c;y)tl0mkATbsXb3o0sV$KcYf4zSGWQM^) zr%*F-5u(zG!x}~e`LcHM?{}b(Y&^U0B#rV|azoth2uy*HOuhOi#j7s0Nn(x;ekL7vwQPUMgu%{W z=^}THkFNq&`~oxNylPG7w@&Dy4vN;Xne4LD!^eAZ#vcEq1hcGD?4g&`f-bA>(k{cz zxF$_O3iw+jT-%FB)LmSy%@Ti#PTh{VryA5?$M?V{U@{BKl1qRPx^rf6BF!z{E@YF@ z+!PdVapRB+eA5P7N+NXav%p??<%Nst%w)c>-1T68S*>*W#?Sqo>Z_qLe5cjYpOjST zsEuvTK)hWbHN)Ps`f<@-DR6PoSl8^vgEHiJU$XRyH}P!U&_q>rZG*Qh2iOF<#jUZ7 z7OW*)RHOKETKI+Zs*!+!f$8+oLHwnXqUD6K!Px=Aj)k(XO!m&)(X?eoc78Ep@SsAQ zuwUU_Bs2bXFR=L_&{Ea;el&A1C~bVYFfDE&`h4l~HBOi4~LaCPR&* zyhs~|wA|b{*Tj|i*jdzk>`Z8i&#)#zU23^;dfVW&kD+;0>_RE?6N^>Jv7JeyJA$bk z`17azq$JO1dvmo5tG1?C;7RnfAnw=OrO z(Mr*^p?a=UM2opsB-Vl}Q_vSErH2bZbQI%Zdq%R1+L>|MxyLf(Duv@f?N zAL@HQ*7=36*_`@5CG(xzhvYjoO~f0g?OtkeL3+<1>PP&|r2|V-xE4emhD3H<`;r$d z4p)1t$%ymalaWezq8Q(+KD#C;_UnhQl}caVynJ0FhSAD;FV4<=)ghK#Yvhw8Ex5RA z|BV1YfV2(xmh_Qb#(E&_TakST>7EUO%>-iHUt9TQsR(i6&c`99+^7c^)OfihL2{VT zw;F=%qZVV;&dLhOGkI(f=j__FZbs-OwP3fIwJ z8W&I|QQC8QjQ;}$4X?Gu-qT9$REW`J5+~hY9bW4y&dOlZD-XS6H>D9obQQyN)A}l*LBY|S8?|A5Q3D;2(ns0*iEvfuG1~u?)JN! zbAk`5e-EMObTZRA=sxnvuw>NDT*?7!sAlCG%NW1<$Mi%25nCaCY?aF(WV>;ojOZil z@H;L~Bxxt){S%0K1U>4m)&`7+;nkH1wb-6Jr1-W=iHyGQAZj8O&mdy~Wg;1kG}8XV zd>pp?IDA*Cfy+={aon(K|7_r;DuncG0sa!B^`gGV%xvU7yN(9?j~AEJ?86<5&qmxw zMaca8&IXY=oA)N8uC=J#9LiyfB;}Ei`EjN*3FUFWzy>|N2SdJs8O#a zkUV*&ex&foi-NU_#Jd;RdZZD2QywIG0@C0ZT&o{X=D?p*U~3zsS6*D{_1d2T=U=dy z?b5!WLJQ)g1?iKs-7aA#^vP8sC#>lYeG=}&A@>6q`7nY5Cr{i1aHV|!SlOhhCh^Jz z{#k4f%47jVNiGGuKY;q$g_AY%;Xc0jzE!@>*kIn>s*yZ^7Okm!4D<$5)pHl@p3oP!JBruug|_G#40%23Dh}?BnmQ1eTy>CqI0&}wG|k~vZPct zMG_8Un^p)y=-zq}^?Jp)B*CWS;pQ}qiK$P#flfOMxGYJ$&$coUI+2UTzL1&rNf+RL zz$;v+kZ&XL!Tuw~*@F@dE3+%LN2`l4#J-~FNRlvb|cWiJI22A6&SyYOA93-zKuBJQw?rT^whxU#}}th z3BvUa;(kBM;p6Fx%*~zb)7iY4J)KV|xT+6fge^yB+_M9ug+bjAiNWBq*=&BDG4p%<&GalQHOJkgJLO3o{?C6bqW zzkwy{HjL-{R)!EGiItKb^LkQcW38>P)qGOD12L|L(2z*BK-FVcSJ*|Hwufvb8|=c* z$1VH)C353FBjYm4oSSBS$426u=v}VWm$bJV9^xosyqc0A*v2NFo#iQSe`z-LjRO4T z4E&70@4}OE@STT|r~z4=>J%^@Xu7emCv#U$i?P*yV+<~Yi1GO7f@`R+uAvE8)#Ks` zp*@$tu(z>I1`Cj~JiAsYRgpuITX15TX77;)bfKiMx;A0(LmVurJULfxe3B-CSZ@h$ z;aQD-|K&_;nnKOSbo{sb(!KWznFW*%ikFR^YfMR_GBgOTaCM%vu@of6H*e;_t63Kz ze<4(bL_D*NtPsw=e75dMey=9oUqA1l&Yp%V?PtkHJOeK0^T}Mlr^-IYn&^ccfxuHt zcP#BC#Wd-PY%ob?#zqtJODyBNn}*NJ8h01@SCB1Bj+X=%rXpE?X@Br>xe;r7YEywi z|09}x1W!mw6z`xPyDQc~f1R`QaDG!Xk!*W2gPeoE+%81WvC)AZAp?^fkAc5r@*C1p z$#8Jviv4?SBXmAW#^ zI?4pK!FXsv=x%5tIx7obPV1%ZUiCKS$U2;}8xlF<yP)mk`xYy zB}{4y)#rt0Ys6k~IH;;}Wb(Q1Lj<_!|Y2l`CF zg@{)Vxl+LypqFR4f;_K&4A4JsplBL#^>7OvS~YiyJ)WMF{QJg{iAe zWBB#!6BB-&FXS~75C_a2b1HYLS#~UrpSG_fKqQB3MBxPvlcUC84~d+cP^EIw?rx0% zk|TS$U3!>~*2DjPwe6o092)_xk>wM;3J(%+qVk5c!NBF7Wh#_$3!`K**cr^-Yzwk^ zxT3-&pycVMH0eW*BLvIHD)70H?{vS_Oe-o|%tX;xO=w-Ho2b~*SgF--mwFH%-x zEtES)|7($NT}S2XO`L*k#X+5Q2N{p(2ppg}211I}JeuS51Da6QZ9GR82D}PIKCm|S zwOxyJ(YG+Q5&t}Szdk5u=Aqpi*(AiR1lcGRs%veA$rW-Yvt=g5^_7;tA*%T+IT4fb z9F1h&HAPPO=$c6q$g`J5Od{}z0T~;D#$vlfZl8xm8rseGRC;|Wz{562=XW+;KR54`+N$_1?}v(%UHfZqh?>h)MDwwk=IE)f}fq81*LWF z##-QOix3DUJ8Z8&ZvjMp*wa(cCrwtpg{UrH?pZkC1#h&l>Ym~8zj(f$Sj9eCVmU{4 zzQc>r&yI2aSRAMyP)zi!mzPLp2x?(s!_`;2C)$ee8Ew3VHi0>Pg$=VcPGv{+N2vzzhEMxva z;{ZOb_Th34<{@1Rq018c&~OZVBzh_#orwl<^|i@kmm7OZjg3f+Og~kffAGYHrn4lo zU4s#W-&~$?>y_0z|DM+}`-8kuKysd3tOtV7U!aj7P(2oDET3e)9{VFWIzRPteG;!A66L_9vZ-bofbj6~ zNvR+339cc4!Sd~f!y=RgsC2E#OyG@cwMKe`BAJMojcNoOxtlFpPU~4A>Q=Dvyz;kE zSyX!Z{`vPVSn&(K0c#?&`eKN)iF?}h6bt@y4`FYGG7S?pIq0)*AQh9X!1B!(&A_9B zt;Cu(3nihuq${L;w+&=+{%V)J8Dzy|&TtL}g~a(IE3Q7~=kOtDVsk5;`U_urifxv5 z3vmj_*{i_`bxG+@?bR1~JuLFse@(7Ol+3BK=u#2Sk}b&mey1 zY^nK|Dmp!KP13xlB1ENK0aSbC7lpi6zEel_qupT%ylpG{L$x2{FV9O1ebtcW7>B3* zdcd~xj(CRa-d34VxsH$WZgJE8@x~ja`@-!M=rY{1T#8x>fRK;*$g6uL%$rsbUA3!y zDJ$q|}L}fAg1+ zA9bBjd=S>~|CKMz;L>9d6&7KGO9+3eF_vLezhy%GE3J;8`L z&xg4bW6E!<`NXJ@CMdA2oLte=ppO)k&OI1fHPV-n3E!g9`4rWn=H}g3_8yLT2ISB4 z?WQ*1w9pqa;c+uxW^IS-`Ah6Z4`S#X=)JGbleM#@Rhw$V);hV_?^m_4&c}^awJ?=W zEKaG@>87R-Lq4LNDt%v44Y9~<9KsW2J)U5c&6nRjSnJd~LYBj2SJgc*H}m$JVwVW$ zFLqb_IBfLA^SoPd;h5N+qH9qU;;qatr6TxmW~a_(=?}lDg2^4Xj$+ihE`UNMZND`O zfZ7L{2b!UumPb&ntBeQI(^Jy>+%yH2(Jq09%lWIhCVA?fh}~2fId1bV(m49dWR2f2 zg{>C6;^8cL`?Iff|M!n1nNmx2CPAs@TQ5J8Pg@_Ku!1(aD23Je=iwR_aXE%|=J^iX zy0l&UU88^mH!<~F22+0Fi+*Sh{v|Vg?=4U5oW#f9G3q_+Pok?`)AtnSQ~k1@7(8-- zy)8smo4jp3P9V~~A@V*!&`?G=Z8`j%67dyMoNG;E*8~AMDQzbpjK4h`zELcQUTycP zvJ@m?EMqJ^`b+blB)hPPyM)~6`fUDFe3X*`S+<6oRj`5?g$xTwigV;5O-($w9|>D| zTxpY-XwGn|=Ve|g*}2u&qQ4W6LL%ZyEvq+Cc5V*~nrQY9l0CFV$>B(^oj2~JH~z^*`v5!BCvGTiGASZR&bqH95nhSu3wp6f2XjWzNf7-?$ie|uR&e?gunH1H4TF~FXA zpl`ra@dsoXc|LPO>Ea&{5D7^I`YneX0M~@T6FetYv;KgVkC;2HYrrS=5C=N1uNWM7 z^v!Mkz6M~xTSdo4J@4)w{o^>jeQ5Icu(s7@ps}gIRn?>0&lwc@PEG>2Kce^QLD*{? zNF#r@`_o?ZB;|;5A`qxgX*zHN(J5RB-uVq^iGVw2wzgFUkFk7K(mt`;F?oOk2s95f z{0<_14}!|{8BP@bV{Nq}L+?Kxz_U*aK;~ok{aF~1{FEmghz(bc7(9PH0^s?ej4ah7 z&j2JKkO*7@#upvEk@{?hr>Q<15NQ(NO|<<30xV$9l3!g~21wENVM+;ye~TVxv-yAV zcoX&j2FyFei%0QYMMoJjna50a48aB!G>j9G(*c(&6KvCTw|{Ym+12jyO>U8RHB!nFq7Lg!u&et^A!75F)9gBBBrn zI+>B6YUcnrNY+ePFO_{D_i+^TBBTH1NS$l_{!=mdXc0kO4)6aNsgV4Yi&;mD^U5et zyJHO?Z~+L$(JpWUwXF#O0Q*{Ea&QDtgZO4Gvwp{%o~rAAPSZuf^9Fg*)Pe7VaGr7j zaWwt$4W*Db`D*ENlP9EPjcu2gEBxTL;Tr=5)m6Sm1?3f7xUld2)W)oX->%7g>46Pp zv$aOlJH#64N$Zu*dS+?h>j6CT-l{DIy+t~EnS}WYlul^ayQKOml zVWO#aS%&`{Mqu4!!7@M(>}zQ_Lk0*y({@oi`SS=hUebk(O7EuI5p8Vk>TLzZC?!zOB{2SVe;-9q~dk~}T6d?4E>?}q8YuuS=h4pSj5nJe4~bIZ>%d=mzD5v>dymn=U# z@5`hBniWu1@Vw`{HoB}6S7wM4B2H^_*Q{R^p|HXvI*a`GdHy0~f4-@RU}61|zCr;N zCy8)I!i$|8*2y*j6?YlOsk6T1ujvp*^H^P^4L{wd=7D;J8+i+BV=Wvt)p&dw28;h_ z%D|_0Jw^A$9kwTLqc5rnUSXdIgs|2*CsU2&uwfX5n{evE07f<7;%{(~`tP>lwngOR z9SQbkJ*7R)Xv}P;PJdKTeul?@hY_SdCT^l4XK-|1W}r+UVZkO=ZRr}l3t9twujaxw z+3z;UyA#a(fwWF{Mrpl(QknwA1SC7L|ezY@;-8lk2)NzWe3dDZMr;0OXO-#-jQ(g;N7EoP<=NGWkwU-Cg1~ydvOQ z7+?K#hMQ3D1URHJ%4jxzd|biL=ec*=W1o@r+UExx^Zp&k(&x(p#2^clo}#W<+-=P& zl9P&gr`pXAx;s)a6Mfl)gL!r99*E_hg^6b>lCS~m6;Z!V8+?(HfxDKG`mh8}1$Vf4 z#TKC4t@QBh+B=yEiJr>QFzpAN8NSWcYpu@D#Q6kCq2CUB(Rr~<;Dxc{2&lhv(z0FB)}`Rq3h3E?JkP9)-3+530-2z-c zW`s?qLL!)ci9;ZLCX)@cZn+T!AsNBN3P9(eQE<8d3%ACbLJLsCdfMQDWYgjP@>SS$ z6+us*Y+jFdzUy1u`z9(WQe%nlLs2pxf;iWG_&O@?T3WwDu62*t9!5Ame_$2?=`*g3 z8)S^j;SUdvFWRf$95$yKFJ=VUFCXj)k@ZC=FVa)1GB|Y+6l7U?ZLTXmC3w)>2rRwV zrBz9U23~3J=Jm;i)^E7weKAQSqE4l#U-;s!DH<%H;-Mo>Z^OG zXWl<4dHb#XyBgU?q1{bUWECI{k@ZTqs_L4oY`XwtF(4Tp){g)r*v5zg8xypk@TRGt zEkbPiWYxD1a%O!U+YR#LmjL$%i7rQb`_30v1hJ}zv1KjubSs7|-tPk#`PicN`u3P5Lp#`^=$Z!sQM?{@6UKEz0@*qcTpgFL_$_$7WS`mkvIczgaM453DL0*_!G)X3 zU=DY<#IU=&C6kC<2VmyRNt1|;b)H?-KcH)Oex)Vu*V=eRG`Rl(5v4;61b#)l3p4x! z>g|jG*a7>IJWP`8dn_XdTmQ>-x#)LsVKGaGzj1#+vnb_4#J=hm6`%lrepYoo;!w@* z7kKP2w7hAx)b0?RM-EbL*>71)JPg102P6PAI6Jk*+rgPrSxYM72_E^-vA^H9awmK0 z@bQ-RACT(xKOg|bT0b$;`X!IYz78NOU?O&JCPD$j7&aJ6RCjk7_5qja?XhWRo^`4} zK*3@GyhXnMp$pKR`z@+J%&oImErAa8xCe-bJaat%ISsd-CbhmiZ1a6yqOw(C-*Ffo zL1zAc=i(3O7dTx3m<@yfr5m1~2wC54k#be?|2!L7Yf2aOGQXb4Zg5`!eAEA!ZoPj` z_cQ#}Zw=KkROYC6_int1i(HO7_L*1y&iACOt;#@fdEv?FDWSWotOIz&M}NSVqu!6S z_jpQoFfJMYiF z%T{=LcxH185FqB8^>Q0l%2LPYmE05yyWAp93KQn2-7wrTxPs&$lO|_YF?*uOeJ%9H4 zogirA6o~uL`|~z-ttr8m%<7q5hR3sD1{diby}xbMBAny=h*`m?b?PoUEGYO#%I{hw zzN~IMI;9RHV;<0dz%sUOXb#&3y9X8|R*7c1jf##LBEG~>&XZDIR*?nlfQWORt~#CI(YhChLOXdzk3H%W1YoQ zvA+><7r1hK%IE372gIe{=$4^69`QW_V5~Vu^2yOBwe0)>rTpy@G(z9;w-10*=Nx&u z#!h=tm6{Gj^aQob<2?le>=8ASpJqNDUF4Uee*rM(Ch>rNm@9~lJ|XcBD17n{C>qen zeEDhWcSNt;-z!!xJG2)g{-cvN#aD>eR6v(!hv@(XdTT9iz%T~P^wD-dLuL!q{sBC0 z?+>WzUw;AZ_Kx;@mm)3k^;9p=j0b3zRL++0cW{R{0ULp3&HHCrLCVeFe;%m*^MeJg zVkPnb`)XBxp+K+5{+F+{{EXda_;Iw{j8kfWsPqoNas;liPKEIRi<#oNbhrYd_sYAY zYPUOg_yTxNyo;So0uN2#r)ueuNsNCtM)pSc-)0w@XT1&>_NyX1w_QO;lL&<1S)M;4 zMgj6#CM1DpwGgm&w>D|aNn z^A#YTW@|0Gm?_UQ!SkE*>nR%to9eH_w|(oe>m7`uZZ^?yjW?1Vbe!1OMs49dpCbN= z%NgdI|%1gIs=m~;0* z_swzMMsR|hCSQ@YPG3AaU#wE@Mqp^MyZd&NQ=sd?3O}JT@G6E)b{Sc}1Gf@sU}}t> zK(nk$$GJb4s;|^NU(t_Af|9~fK03YZUPvwoY1V-PR@fKYb^myx@*!()nYs=K?T zQSc(%&`mEwWM`{STzgVK!N!u^r5b-jZ)EICg!n|Gb@<~AmhHN$^)Qhdx$~^qV}8Gj zNS-D`d5T^EZVQCLcuXFcu=2FdFm4N*pNoY~H2a$CcSFyFEM_9{SaZ&;_@vYo#67nU z@^SS8>M`JWz8NW}aC08fXIBosqdBgor0DNW8vMku?s@e0{UGaielpGstQ_R))Vr%u5S`c)ZQ6Bch)A8-$LhKds zV^ zmn_QVq+^9YQ*~0!WPQ}{mzB_IEoC@x5W@BcQ&NIWl4n9DpVoOSi~%5`?DFz9wOC+6 z*gJ}nFjDV~3fw2L{sA#AmrH0yMn!YLB@ff{5e=Dd%i9JmqE9j;ogn!p3SK45iRt-j zPvK>TOH;b>G|B>hM8I!%@{Y_hEo&)FCHq1XYPeytH4p!*)-MOx3Tr{pSBai7EDn48 zb33oJ5RPl_t0jb`4e#WU%t4@XlD?)zxmBqh>cCoztK6xM;PnC~SPUVO$Z%^+Ia$oS zejo;A;5_qX0s(8k(-kJtI|FshUj7}B9q9Fn4e3z;7Q@`;fUOaETi08cK90EGoeLtE zyw}<=m)&(R^668T%)*4Mc^lP>`Awi%ky|^o8RDuuTHtKK!H(dMYy#i75pu=pDQRW7 z);ee$F5t;*(^6;3Df?MNyTvy;x< z4A8$bf-5Iu1EH3`M@0|};k}Lvdz!eFRaaWS{u*tke^oszetK`It44HHwDm@S|Awi* z;3ttQ@bmQD;={M!zWbEXw07#+6~WTNz_Q2TDC`WIQ%L@Jd83FJR|tHfntT{65b^|7 z*C)Gc9AbfE`dR|{-zrG|Ys3K%T_4rGq(s#I6Tmz=1v>XB+y!9bl${PgVj@9idOUZI zRNT1jihtP|)geq$gq$CGl=FH!7Yu5K93LCY|Cq*kGm9GeIt zeRo}VjC?nYe|Anus@hOj_#j5j(lw{{u-pDDkKb6?vpXHJ-6ukRr5iyEMopn2>un*{ zaw6FB@4LzA4}9XRjGxD4 ze(tdZkLq9Oxh#$F0N5m+Y^8NOZu;EaHX)=nBkG||KKw~{#HmGuwnx<%v06U!TEgJM z&w~^PuXVZ%>w_9gj>}1DSp3^&`!QxK4V^koa&AT@dauFQLs-(Mo@E3ZkSOd8YtQpw z%ocSJxHz6bXDL_gw~SV5z8vc&iw<`(tlfiKFS-dE7o}o;#gN>WPI!Vg_@u0=rSh%| zM32v|ToL}blJV<M>b>1tN?SJG-C9(QZJ~h1r!d_A|v!^cSOrkOX&syA>B9V-=GP37aZrHuYPK!y;Z8BQ?$>6xg?y+>#m*zqekE?)7aHO0 zIVd`hycqEP6}D~PZohtwS9JMSTkvsQIUnwhmV;4+E9(t=+i0?}hn%IJ<|NX$wr$S} z{U!5Z{`kyZXOC2($P1&V#vZo}1M~+yhl;cqjq>3;PF;q@f?eauFd>L(IR%H0@_>8$ zi>f#c-d=DPqCBLgIYeLAZLWevCz#qe-H?y}bYjPaYG?>G@$D0^tWSrGM15GiR7dC= zB+G?XSn(If#(F(4??=ioPwYBf(J1KufhrgxPlVL>yKbNB{BL4I>S{}B=gmOj5l701 zpP;J$P#be1Z$@5dQH?yr9*dqf;~7nj*jkZ0jC5Dnm)dkOW5!RZt`h%% za)6u;@E*c~PJ^;HzC8f3J09`?5pT8kok0L1fL+8FL2=JgHa^4?%7oG>mTAGXB zNd?^grlj|Dv2mQB6LAlYi-3+^YYe?dqL=a)1pb4!cK>6fi8hTK0C_h{5P)* zdz5zB}lug{30o*-6j>s8cduBWW&I$@VvMlmUet>o~UCY&bAC(i!3Gy(9<`8HSJ!wRmE%&cn9Ms3Dqtbiot8xAh_6U#8^7 zY^25-KBuL2Zd}ij-}VgRJx}tjrcrjpQ34e*sbZ_7UH@bZ8)@7)C7KKsQBF_vFO+r? z_pp-V(rhQ<15mma5~_WN+&1cq>uO?_hT?pCDv8&lNh=JBEuJvZvswZ790Gl7@V%nK zWqf4W*w}^)Cyr9vo`HFH)Oe49yZriHaa*^f%3E;bID~$GJq*1()8$t-mJidE3^7H8c{DqCNJBA(CvV>v{0Z?3v zPSb60icTBVy*`#!m|vSe=)4;Se`({iYAaMqD0j{ljvF+Y8nzO*sv)*3wi_pBr0SN9 z+D%wLocz{74|a2Mc{S(!Iy!eW{%4Dy-1R$==Q6xzeT`i>6Cgb*J$|bt6f{Qx!pbt; za&!?pae1v-uFlOnGqcXP^mMvD@o-I992;KXsKV8dYGebMwJQ8=SmWBSjc`@;A+wmd zI*e;XW+3mk`?fG9uh445v5PJ9Mrff5_yf3S7n$UFMH?hagmVsh@xqzo?GI)(?DT@~C;DH(%r&&i_J2_0NWQIO<=XYi zv|Ee`mt0#kHem+2T`Hu^Nq_`|CQD;}Y%5!yi-;UjJ$~#zOz4vt%Q6D%LxI~j-9n4=*XxA5# z9vU!&V;g=99?UIoHfZA-1-Q*}TmTkh4N&2U&U%#6Nay-*7W776{QQlomzQE0_csr9@X$X5RpZg4fYVkz z;{fWs4;-7DyPGBPD)hSV*WtN|eE*=SJ`A$2Bjsa6YdMF2_(vk|ZEP&l9q?Yx zT=-PmdSS_MU&`;ct~8Q}<4hNqkbK1{{uNl zjpU@wm?c3gu2gzWLucYz=S7lEXS+7X`*V{&(_9q*fH?lO!R{V_W&lNkJRGhHJZq94 zI#kyI`RBi-bShF`eN?9%9s{(~fWz!9;H8}2Zh6TNZ}g#Ob|LVL-ct51bbtd;LeZf} zef4h7`+Fk*;P^zIUQ2oLB3`SdaR1;4-ywnQe%;6N{670F;=2t1d|Y{d(vHJbo@4Nd zVnpx6-~Rvr@02JL+F;r3+TRnO2SOYUE&bXh0m%-DxW5hk0l8^H1W@G$T1$BefZt3a z4Eyvi1`g{UfbKZuI0D!ZspU#U*Eshd&}M%md#Fp!Zj36nOm^kT9}xO;998lUNKA37 zV@8bu7*(a}w9}EXUJ-z43~`&{lEn{?9#EsK3KRogm{F#$Mn4`r>a7VFHHT{4rX&|~ zuuKm2#GZD3{tWxUyd~)Eudcft`%z`8Kg9tA2&gB#hy__t~`2$tFoJJU8C80_usP+WGq9p>!+XkGZN22+lt zKHZd;>15G;v;T_QDNfj~EQ^+l=Yi@k>E3unH0q4|bm^>o=lsblem81_y9Ww|I-a)S z!m!Gnnh?X-QEcP3{iJ&pnf%TAM!V&2SKhamz8!y8@dX+pF7e4P+W3CQOln z%MFcthcp1DuBM$kG{jV}qysC_+aetD5I0NlE8waH8e`99lQ?+b~C zuDy`GS*B!xPQiAp`Es$aho|cs-2U>E?Ja&mF3;$&*5E)75>c|Q;uuhO3j$0diQ&qbA&&&W4edy3~$U+Q2HhRMn0*rLyy4KsECh(+QPw+RBzE)50IC=mOQnAQobvI_JkyCWb3H{P56WIbl6ChDTH^G4eSHX5X^_2G zpfBwFXJeumNP;T2iZW0lnFaVKb}B*2(%VT7tbZEy@8kIPj24=n;zO~IaMivs zX3=;il~eahe|`3?cq9_6O9_Av^};3fwDiSqg?_CI?>F~#%P=JutO^z@sA1&5z9ND} z3ce(ousnRn~s|#KH*`OAKMMaIk2Sm2NI^Gzm z4|t~SvIgJhykEc(9~%Iox#L*|^7Sl=X4(zO&g+w}-*nH#BNW+F_>(5jJ02H0lkyPc z3)A$Kb4=cy$Skh&+J+lLtz6iPwNV$1qfE*-i@MT6KElWs#KdQeO3`#;etPAe!QI+^ zXxckjRc1d7_$tQCB9nW9RWPgH9I;}=p7WS$qo~Ev&LPgtVc#j$9^-2%2p-e3g1aqE zcozy|6a+d~*}(#I5OHw+N--RBy0(J0hf!woM(P;P%^vav+2~ZG&vq9KItUfA{LqZ; zsCgb$4#{wI^iu*`C%itHsIDz@!E!-qw{&!O$z>$N(haZZjwZ@p%6MWdQP{kCQrA)lS@+Ng4qT z8Rd9gwD?!&zJ(aBJ?=RRXpgp>Nm2tnPRQGMe^Lu{Q5z`Kj=ZXE45$SNzVkpN?Q|SQ z0@AFH5uGb)r>6)-NA+r4uV{HWM}be7oOb7blvBU+?py*M(_uImp$NMAih&m>S@YWB z$pq4LEku3oal2W;#6nOy6 zf57mg>M~8Q7T4J@ATHXJ0aW#00iSY?zrBl3{k7Tbugx*m9*GAV|CG*sn^{t0Z{+XT z20m=GMBuD|6Yw>GH=cf0tD8@Urzv78(~GXCyKI+;tQK}8WLK0k9S_%j-4kWF?ymUoSOpxE^Klz6w=!nFj< zX|zgY=v*xszPdV-Dn*g`tJ@%ukZh{P@5bcLHC8Vyozzw+5~2 zVCH~<=+2a>DQt>P9PET>&NURDXq?OXP92oLbKI}q4U$f%tOK_d%XkJ6LTHz*%h#KP zlX!xKbM^aQOBLRbU3vs|7=EV2tufAJ;`a5ob zT7c#cl7Nbr8k7#UYKi%ITYu=MYzCp+QH;ahxLPaoV&adX`(hkcU0MEi8BUHP1i>zz zRyxhfaXl9t?GZL>=thdg4rbJ=R4`@;9iR8jBI?#S)yrHA$tJTr_v4_ZHwgptutexD z$c0-P*Wr@k`qr3kC$^`>4L1hsN-4JsPva`_G7m>5(>l^8=CDp7#%uf=zk*>#s^Z;8 zg~@W>dF($&e;dl)fO-f&d)QCW<@=!P#SL!ku9MaIo-8FQwZF$=gNzlguSjdzd>%*38W&pC-IYd9j%jnrk9ln9-1U+|#58!N`@ zEbp{BKlmvqrH;l4K<8y1H!z>4@Cby1-0prh0mr@XcksXH)&EFkES#(y9mR!eB+$S zF|eBz5N|z_Txgx8UYW!%gYxrzHtf@Td;$1=gDXrN8AOlaz2mR&36B07>e9FQ>q~mC z!6ha-=G;?P6z1XZic)r_6l;g*>nX3C zIpyyGo6zB#Umn12)!P@OTCF@qd#oD=1X72Ded=B-fj_SZ`M_Mmufcac#mkKz+CO{z z_AXxte-xKViiVSL*+WUsrrv_nr-X<`FCp<8g8IP~v+=^lkgke)8eVU-fj-7JLkM#v zk9&L0->rFZLB%YO%eB00*Z4{N9dOaR_Y)4B)^<|#PI5snkvT!%x&`r48f*DaI9TbD z30jQWr^w0~){YAV-!{E&Kgh+)^)?MlBsbwj2Cyt#H=Unvx`on;QB%nBj@N&b72vkTo@Pj^Cxrp~bSS;+2^ariiYs<9lAqc_|< zI$+d_!FhRS2-2sg+_0cbs!L2P=O*v2U*n~q!mw%MB?~%Qtwg%FCitiST z>F*aeuN2WDnvI{{JnWTC`U7&6jw$9LCniy`61S4(S=$DmaplCrW)J4RvbW!lHS^df zbY!RJ?kFR7uQn|01qaMM*fYlq&vk=E>{bNj7fR>1Q|1;}k(}|PL@0B*&j7E%n$0#w zjP#y$;rfS!QJ)GvPZdC~iq)81OR_{~zdriSvlAy{P%u3$@Pr~BI1!a!J%JawM5|Rd zYh3id;xWi8R7;V{kfr7iR9BZQ4!zGW$JzFa_@(mk-mFm2lI369eHHXrc8&DLn3Tdn z^-Hp((VKmVhhE{Ix&3^}Q;d|C*T$O%(k)zF-<-=7+_fx8Hv+Ii^@UL_C`Bs38DS0g zUO6|g(I6-!kHe%l;6nTQY8I1_(~0bEW&<|b_Kas&#AJADDhX*Y_|VK--ez;^?MK(c z$~e+CKAL8#2KT!yklRInFWgMk zX%woL;<^mw8QWhwcfIz;@g`?B6^AckT{)x4g4OHcJ0QupwBxp0yRfGl8<;1xSEUEJ zY_|?@D(3tbu2i@%qhmrp#-{&DRtUkXLf6FdoFI7tK6dBy@J@H@-NmL6rwE#yVUeA4 zj*KEU<8MN7_fB?xhSVhCg#@*e@5%mHWX%@MUFCp$Z5WaAgAT{SDMIbJb$U4dq1)QD z+Ph;Y=|v)WTR$B`XR#0w44;Kr6pZKAnz%U6w2chY-5wupZB}r=zZo;|_NwezzNe-B zP4wGKEhpEuafvpdbS@NLWxBfm}|RFdYvBTIzLy3S;PF<5GvTGdZZaiA?czX z#EGX@&duwUyYRF4p}SArv~Xvm+)NWo9(VnG4VvF7GOx7Td+yEQ1T35PtfT05G;j2c zIps=TBXb|*@sYvsl33)?NnxV5% z&TDXOR!tN8{9I>ReQoz{j_7RiLyCRps8{((|L2H4f~{YUpthAQ4kDR9X6Vx-rrDG1 z)YVPf(~9mZnEBbB7nc=%NXvwpfMxSM+ya2yUEtLqd)n{bi|)SehB`Z}{T4)~W$Q5ZC;y9X+Vuz*ew~b5^D)*1oZN-Jrs%R0&;zBLkIE zfdr_Z4DWv?Tu-P1Y`(WIC-+h&NtU3A`}UEiIu;``LG%enIn^^fp67jjrR;q%4TYkd zO_y#B=oXtJJg@l4vQ0a!ZTVGIDX&)dZl z8%|Sg;`u4DGXj){geZkZlqMej0-7BwTlpCg$YVg008Sl0NSNeV1d;SOOuqq&1`;U!4sd3Yk+6jxXwg0s7f(w8P-I-ZbeHIoDs8Muh zHo<|HeIiZnr^Jysu4T{UnH?agnLK)4ZqNT^CgcBYCcTSi;+FT8>Ze9P>o!J2DXbqD zcBAUEh@->O1*`DHgYmGYXA#YjoKN2KJ-gzNki(QvANTzB*2$0uXHxF|>`?z1o*m@x z&Onx+J`YUUo@caHs)T>xI_5wWc!NA6uTVDsv#T%jVZ;moQ(cLW_OP?$Kd7&+WMq6w zkrj#jYy*MQopd<^^f<^lox!40=R*b;!nTx)h@Xqa1`KElgb2DP3G5!8o)Ww`xX^Wr zm#Ae)k=#hw$~956-5llNe#u?C^niizp$%flWIe%rgsg5tMXzAT%yg}6m+oaTA7q$H zPYTag4j8z|>noD?J_`hvcw$gG`z#KZ2AZ*-7A|8SRBxTuaOFO)fBaNfDX-L}5<{ZM zOBXYrRHf3JaR1^xg|S_aeAGs){#Yr*QmNDpTWKR4V!HHYcIJ?EnY`3>)sva%$8erF zwfTyotiC-xJk0+}DJZ4mk;a2J^es;3XWySV*+t}$rMb-;4ki~$XXKB0nwAE_`1(g+ z80XCfRI_vgkUYuHW8K_&vKq!i2(g*e%IqH=(Zf?NJ>LfFWX*B8w@>;*nKZ7|J~?Gi zoeG-SxNORA6_jHE>N@^qdGy|RC2r7>%Df$yQA@+Pmk0KCiL?<3ZN*?^es1>~Je@8W z!U=(pWIe4^G&>8qEn{S{15sD(YkuBL@>?I*JqZhv&T^x$Psu%~TUoDIw{;2WJbl=;}bt( zF%^4v@`QD9c_c*tx*d~8H)A13al83!ac?*Ca)lAoOE45OSE8U)ohJq@64KKRm~4^V z?$;I!vY4(z{bUvTQq{gD|EQ?x<|um+`}L10yw5e)#UBEw#Kk(HVbknxuth|mc3_dB zo1TJP_*0Wc6p1-3W}zyfe0T?EW_f;aUs^`N(S=Z-K$%kSb;X<*BfWaZHU0k2?i z^8;_7=E)e9;ZUb%D;ZCoe_)-IVNdoJEVdm1mr7#|c07=ib}v`L&G`xiYu%oVKMm+# zT=S%k_qPM5tLXBw7R=*Sl^K_i*8fta;s-J4~w`=IS9Xc8o=_EaL zd`wLS``(D;_I=qNnvjnxJ<;jeeiT-3)W`_>MSfCPm#0O*(w{R8L#zU{vNov~2+ zvU!$|FE*t?_4bjMy`KSca2Fb7vv&Ya`d8p>YV8Z0NORutAmaUdkb}Sx&Z6;cBlV!; z(EIDjLJppI(1iq*CgA*-ZwJ~N!09m`XPfro_v1nLzkLSoX9fWBJfL)Ph9??O;$eO` z@$YoCgTT-CC*mi8Y&^)D@bjoQ}MP>wstf zqcH?@E3SHk)d02~olI;U)D370DhM98l?!JEU2zO!*nAI~;r)x!c*o}*{0pS9V}F0@ z$s+)|kzjv+()v-@DbPLP_#!y=3Y3P zhIE*2zIEBeZ}-luhU~PL(>8J~zunS2sk(HYSsDq@o5%Wg@R?v$0fR- zYTu7l*uQ>W%#&r&x+m*_GrqnyWhKrqGxytP=~P?Ge0ca=Uz}knDP3*jl&4YPTvmy= zF=C=ANZduPKF_tS7P7X(oZaG|%uC9QjH~ZRkb&A?b%wtB?B_Sy6lj7*g7H&Mih-06?|3EOvy>++uwU2uk@Etk zlPDiOqK!>Qn(Gi|tw#z%ES4uqFH6@Nz1D>g7g0qcvWb48r; zluAo0GG*<)c+qHw&O2!t9vh=^pWyf6yOXyn$B>`f$v!nIt~KOgH`@(dZB`}W;(-S$ zaCp&>3Z?8=H!d1za`8JRrOUnC1k>^pOeee*!Mv>=g+#D}Y(MG4?l%KTt}eKVrsy|k zhihg`j)O=f&S0;^cN-Jo5ApV?tc0mV$E%ogU^syiLJA;DoPOxSjxDDx5A84+|6FPO z=)pHYVW-@q7oA^WyzoJ!%vqqi9tW2@HX7PF$CZ?MMczZGnw$xMdA4J9Z{w_%X9NZJ zF!DGT4~(XkQ2Usc^hy{~p_XsJ;9*xfHW?qj5)&K{pR*tfyHcfgXl)pqhVtf|CQqB2 zJ>nl@L=W7TOnP-%zt8BAH%Oo1dQkAr>A@wiEU9dLDP5>gU3tXnn5jPm@e}+)J2~Um@}sr0YBk&@!}J; zKAk_)MUx)NFvHI!@>Q@Gu4$@+<=dolXSn!DWrMXCL+OE$ytoQ(E8I`g8ccSe-idF_ z2)X-8g(IHKny?sj9lA2kE~Av1JbniJf)S3JzU)S_i6ipBb1Y=hTo_sJ9~jqmh?|+2 zcj$&uydEF*=3x4hQhDXTx~zqXA|Ledb`(a?4YQ`L2Wq^s{#Y;dTb4Jq$6@I3Yt<{+ z9Lwbj@->eSZ)Q=FACmA6KIA$0YqRtLO@6-dT2y7$+xo%u$|&oy9M-D-3`a_mb#WmE zL;9Y_7RVA*u5W@!p3Mssvh%$c!7#2g29+8kv_}M;ygmEu9G5j16z)L3KJ~S|yZZj^ zm0;b;SU5IEHRwl1*spd9vXd%{DR^jb zdciO!{zj5d=jXPz&J5Yoz&?w7jFTh4JBSO2M_1G(S@w6e^c&pFcH8du`q*-%dTrdJ{$WjgCP8X~Q@fzd6 zPu9oC$0ra$K>kJ*wp5=&`?7WWkox!6L%+a-NNSTW59YrFz%E2`3BNxJ*5qgLun;QW zC;1eW??Cl^;QDL&MwL}ga@`?-lHz7EufFd5Sfq+RJBGPcio0^>Dr>Jv zC;ESkLYD0Lk z1LmNOFI(Up&7Q8MPjO)CHK-O9_nn<*R*Rn_Gi63FTSvjs1u{ z3;ZmZ=#%43>oc^LIP>0pU?Cy=VPTW8=7GIHtr++E{46DK&Tn`>X28RAb{V<7BLk7^ z^O+!M4bOS5D2;yjd5C0AqKs-~-^gw6>zuoG1AJY*=t`@Fkp)wD|2KO&zVk)_^?5jr ztaKT{fvT8M0d`}&7$VL-C31*@uLlT{1UDmu@$iP5Pja?0Ig`33^7ap?nFIa!b95)t z=AwtV{vccfnIcLGMKa!~J6nDB9TRtR_dBqb@Z~-U7muaU2nOH~G5SW+5RpbI@Csh) z{XeX|XIzub_AVR{L@cNXDordP9i(@$Q3BF?R}mpVD4_|Vs;G1c1e7kJSCv43P^@6+ z9SKcJ=m7~x2j2-W1aC z60a5gl<57yRa8r^+6cPaB(e1B`;}W-iI)ai>EwmIw7=zX2wC()d0X`REi8yeu9n^& zo7T^;4Jp%s-bQ;#)(yT(gRt_7Zu>wBk&~@nwIZBtmo>~UObeK(M&DP9F*CQ3=z15q zakt*_(QUji6O1q0HzBRN{zIzu#DI-sXU)?t%4evJp3A0z?w!n%z=TWEFzeX_2h+(p zzpG?6b61yLm0b19R3;E7tfj#DO(CXxDq^IO~{20BoSZKo1%-tRf*3b6@(ehGpTn2C|D zx`A9{crlnmVbEd<+zsoW8N*jl&5TB0d z3QX)#vWG`!050~*U_qTtC2Y0+(N^8gb$GD<#6BMvn*(N+{fX}5OWv^?a}p!Rs+h&E znY)707E-a$RTI;P;jAeeZ${z`x}Uj@B#D%@6-J<70=CJVs3%^(ZQ5Jn(aLv*5|gHD zd@7?URipjbGB!EF;3{0GB6mN{{yyfSrO&Z-9Gy~mZMV*jL_>Wtlqcb3{1nHK3ycyH z*%-4hT#-EOQ<1j3EQ22%aI=p0(Fk+)R5!LSSxRto%;{32bP0*VWGdq8+n{&O$?6T} zrMS->zgM^v_e9kENq);IiPl^B?OLZ8uErD!`79=xjH!-nF{o|x3{kXvc6j?@&NOyQ zT}t>KQT+Y9t1;q}St)`w;+laPqsgJSpQbD8>V0%ZD`7+B)?b|cMn`={N0UsiB(IMS zR%#1-R35rNVni0sXJTZaAJn^$^2W4)HkNl(Xy$6KX>qcXP?65i;i2{TEmc0gIhLOI z+2r~O%Q4FlY}`-_XW1baw0>Wd+rYz6DF2WBv!B-AI#nkQIPHnzOqXg(L)HSykbJI{ zNJXDOZi948sRhhH^+bj2(IdV`n0;o;C5GLnQVn|oeY1y(iLzdRfLu^Qj*I#hpHJtm0IEho z|0X|Cjyvf37#TE0HbZ6s?GoeyU|P4va50c>94WjeN6kQcbLbvgCJgOWW-=Os39t}) zwu5OPEDwJjB%mc?>!(NFr(Qxnbsf@p)m}$vyKq^L+}13pfQ9XhGEyy#*nLm z*B*1DAv9_VCqW-7%RP6H0xlHqqyn2>4p*ZB(D(oV8h%N&2l;R9cjEuA_B%3{`nqw(p*__+N=(h$Urk*>>fyGV6)H_)djcs@nM_ZL zXAbMp{PfDCCyI9l89^d2he825C{7>y>-uh5!w`T&G4lMla08+r|8ZfD9>}hHC=--B zpW#2ii^0``w7Y)XN04^sKmXHv|9Qw^Jw>L2Dq}&Vq@RI}D}!IcUR>NwJQfE0^Xo89CNSOt`Cz2K+i)t`|) zw8ZD;$4_ew)Y=_Fi@6foc|bTHqJF4sO^Sx~b2KJ&W0Jg2r21`4PryR{hgt+%XrIR> zkvdiyKpS2mB(7v2#pDy3)8k# zL}*s{MzP51ht?Yp;;k#{u#vEUfZCEG>-)Pmp7bnU8%`sv`=0&yono0wxP#zo9Y}U~ zO%tJXxK`uojJsc)u_h|0TLNz5>D%Wt%P5U z#BJu8v$MT=78OUqiK;BnG`?ZvXMcDriB}#kCq$5wD!kcJOF|CjEmcF21Mx@z?UHam z=u!8ziYn}?l(-X|A8%xqa|ZYP?t7O?Z@qo5p@9<>3F2Y2tU{3}&RL?jQ~SvY1j;`^ z>qnF6*|fbuPXqy~IiDdSebwREXQ_mwFXxM#aSb9YiK7&)WYIZ3Sph^2nZ2U>lV|qW zvt(z-uiw%~8C@&GMczG&G)^54he#~l9(*~SP)A&y*0w>LEs|Ro?vvmK3X z+ic%#(9&FfPB-gJnxh@Z)Rv30(o)8dPWD<{OYie16`uK=u{lFY!5duMZ;i_TPRR+o z-pvHK$H>iN;`Wlxs$&yI3({r8b!@fF2*neT7buS|@ytTtt~}Ylv$pz8V8kC`UH=OO zqd@P^J+(nCb(QhfMt?S2)GfEJsSUCSEFv=>s}i;goN$vRyDNV;h(5eQdF3U9geprX z3|a8!*ElDk=S$i?JTWk=Nm?QlCK!e(Qoa#X$C9-RyR~-OkkUGol&J)(Q}hha}bxr zP*Bj=BA$@&aM@rZ;bI9WO>`OUN?PLN@NpF&>I2Ks?$z&|TD;PWdD5rpbgErsaleUO zm~d47O&f8fFlbyO)^!E>QFrtA-qL*kX_C>n*Hqg14%3!JV|GoSx+Lj7X9Te@gLiG} zvg=ZIiDQH7#@6ObNXK7 z9#bz=SN&sYUnzf;7xmp6NvE}WVlQN*7++Ppe?`%6XN+e#PJ}pkOvX$$nTAdNwb)33 zGBHXR6jgRe@k;IVrNk$tvi?>>bO#jKwhGpRd_PNC^i-_O{`lznl-E@kcsHpX$ z(~P2K6Y>CbKhdO*(^8~jSmNe!oW7g}d=h%rO+sER;PcVv@1BztoK~PvLU6fEJgo9( z-PUbYCiM#-Tp}{H%s({Zk&q-+#-z@YncLlc>tQK~XZk9Hmin>D-CDoVrDySbMJ4 zEO|^1My9!|oy_^`iyLR+&Xpnh6r;iJ)Yhpy1gTAQ^Af6G^_z{&A>#kOv zu|tR`R1}L9UqqK$s!>*s@nQ*2uRT9Ot=|*Bj)$=gR<8;)*?mfqm}4GJ!JIdP=e!~R zOx3)1C*Dgxr`&>AfokD+bY6=tjzvsm9d5m&7indr6E%fpY5B5HDz(UH)LR*a=2l>8x{{31~!H^LG&6{dF0 zbspLj4=R?1BBnH~P(f>NhUDua&zlwgu2RFvo2l|!QximVnmK~-sWA0jbQ!DABd9~+ zt%2k8$I~#fKIlR zLS@!mHDcA%us(w;xx@6lmW`XeswUZn&}UHBSDjFog0d^Vo9M_o(04~`FK0}i=rw_NRR!k#!h6#7qr0)KRm0h zi|Qwf7P#cM>-#v5MJ;HeHd5k!^mHUQ3RQC~M2O-^x?OHbhLZGE4)0?YEn3{W-`k(V zqYhUkAr-V3`Hz)Xwv|f0*OnovkU4r4JqC>{OiyAL0ty5#wh0wwVo#LV-}Tbl|u{&0RlJVHcmU`=s5>^HE54 zudVYfrw7foo6}_GM%wP**SEIbdEA&>QR&*9jFu_6T1`5qFQ1%q&Y)v_`*X~?O|yP# z4xnFIJwvhQMQftR@w&$ta z;5W~7QB(V#4&C{8kAGG*^ynnGzY9jRtn==Cs5r;PrJnslqh4XthXJc3~ zny|bJkMVFdgxi+g=*r%nMI{{BY}1~sV`{Kr$J1MV_vEo=*WW%d&04Qf^10EK?HA1G zx?R;CPxVhng^is@Y`erUzCitwYS!paBV9nw@i*BWb zDxq7wNvpIW`SHS?s!Gu|ulJ3K21`%eq|DUpC|zucvY|@dCwR}h3>QL??K1i^mJNOd z3Ne}|5UcVCSskA+Y;S4>_5nuZOurWZ4mHr%9Bt+#H|vgcOS+}xc8OkY=$1AU;fa{O zt0^-y)>4pKmnBH!!GA{nLAD_J<2`-J%JQ##f+tjEm)c+)&OVDJD~eLT2aLPFWc50$ zkwxz93k`a@_H3HOs^$?#-q*zBc>3j9e97hA^}Lbuh}*udQiXD|=BOl(xErl^EXOe# zzND@Q2bbFj??S`DMw|$48ZI~J7DTi~IwP(TZk-O(x{78kB^qbvm5!a1{@t%MBKpXd z=(PTR>l!pbBzxyLj_LFEysCsMf{)VAHi3uSt!y-dU)AO3+EuGFv$)g?8Mc{?yxg;t<(9rtA!k>b1U-S!9a{3o`? z+>L7n%fuVntsY|Y)l*nRy%twE7#GM&aO*{M~^#xTU~gJ zEVMm@6B0eqC~e_S=_56Cpm}~fXS-IXx+J@9y!C(=SPqX~;T??NU;Az`!8L6eX;f7W zOXiE+D3SPd(fq5*90yM7is#q6-wIR}YHTXhft$I0S`VcU&2KM!maLU1o4_-C>FRKj zu-#FUnrGE+WrE%Uqq69|&7P=U&J@H3&zMy42a^^(%Z`f1ni%#oT;(WD7{4r{f&=B2#2GVp&`(6q*JI$2Uo?w_Rktd`$3RN`kh*3GA z!k?elEgQXu9vPFsrbPF@s!N%PR?&U;@;ZR?`N9bAdV|#3>lS)FOw{sj0(D0Xu^>69 z2dP|`-_za22HS4rLoB^#k^rEOGIwWi#SPAq)MKD9UgTHHAINvfyZf@Bxh2iEei8V6 z@=`Q?foZI*Odtt!q2puhcVYWkWf3IIgH04_IB`mqq-vc^s6+WP|mQxoY|jqLY9H3`Rd z-i{H?C7e}^Z37Sz_=bmZ`hWez+xvZx6R!1Tpqoe?2<;zor8@ME4r2F8Fvm7Gn9I<$ zN~vLQm--tT*MA6j=N|&n^jE{v%*-kGFr=IAXd=O_a2NlML2w;4u@->h^(sRWK za-g)Q;F}FVp2!IeRQ_o$7zM}uyqRhtn|7cO}kE8tqu^&vuT>~}S9FuX} z@E;d`%n|=bNI}~*(>r(vWJh<6&D(`6OHYdR?E9+#%&{BC*8gD%v?4V>5df*?KvEi( zQv!x9dmH<*`#ZoEm_=$<|L*C5&4>)}{U1Al;H=VZGXYpl>@v`$TBletMhukOjP+4h ze_$BkYWTZ+>6$3S!CnE)LY%#S1CX4*O5$OpXu%E9dzK{&j>lRzO=hdSWp)1&kJ_*PwPqX&q)a3JnH z!+)M-+KQT=lz;Caq`|@J-`ftkJXFf?pIv7Z9LG%Ut&Z)z038h;F}k)E04G;)QYRyR zLw)}_mgei`n04S~q?A@sigfZ_jR!d9CQ?3(6V)$!)b^n(UiIr?UN_}Ljjk|$l~Eqh z1MQPSGHnnxUM$X$SCdSV#Xp`+U6L0Ysdh>ylvvb^_M^AX)nf?DIsAEEuf`ZRBMth* zc%T2yx^_~kv1{mV{#5;J_p!>PmMXIeJ`ZPSKOVjKJ0CuedUOj7*Os-G4HzGWI z0}7qd&R%N;=b?=f;nE$BmWkU=Ie9$xtX^yJQ3>fQ4H9_*JX?Y6|Fuc83=X45y(VKc z^vdPU`t-Hrg%@vy$VxJ|`oHH%O_tFVdRUxv2hZE(cX58qWNFM~r+&Hh{a=EILpzz)N)(GYr@+VuMR&mroE%TNRh?%#A~$G*fH8GwFw#47WOI1%JsaD?5TX?3OOy zGGXi>4(m_x2elmM;t9KZVGtTWrSuVML{e-yDV~a84I+}%Is74PRC}|v0|P3iV;brp zs)&Z_Al+fteGrKi`ZTrg1)-lT*quG#o(DJuHVWx`d!QF6X*xchidYRol2oR9F9>|5 zU{-O@k+zPMZ1Z)!dx=6)Q~UIZILvurm|ff2Jc&^OVHmRlD>J%B7=Ow7im^24(Z&7E zHDXgw@6ndQGL>5L_J|HRYd-3&jw3x{$E?8FpWd{~zwX?ze6pmMd~%om&|{n7lfMU) z8%XbeZ|g4(WQ=lK77)2Nk6u$!#*|{ zY$z^6VP^jig!|V&$2&HT^aKY|sei_n%YPkD@8q$e)cx(5r{DsQ-TbG(O1CFwqDtyH z+U(rU3~P(@b`ccVphAy|Tf}WlU1Hqdi#IUN0#bTr<3<0x&x?K9Xm6~R;$!k;`}f+{1AKf=ANV`!87%sQghXGkWY6%) zyAHYa;gi_h6*Ai2Kw3ilfVt85tn`A7kEmtR)wO^`j071Hb|}7oAaDTTPBn3m1(>1) ztK7cL^WEwusBYihAX_6om@GSa_c>)}dvn@LbQ+oh* z9`vHwfTe}d+j{nHfG?Sr@s*Xm%RGV(4j9g$LZfFg`hcN-XF|%Z#E&(A(0J9A_^lmw zNW^%MK4E7Cs2c7_YhuNe2gC$3`1^O#lK=hOq*GB?Gnc3WS zZ9?Mwg^9OV^w5X8`oKDG3qQ2O2MZ#2KD^hu7Aj-Q#fKU#j^0bt{CJqnYGf!XDlTrP zUF{|t*C(#Rk}|OG`u)??*VoY^0)&t&{K6V*)bZNo43_B@P{fA|5j)z}%K8d*JJ5=A{+FJMbdivkcYyJlX_$h7Q6S(a; z=IfHHAIdJ&#(fhi!|#HAc7DRPn(o6Uck_L(WpA&7OjFfeCWnK&v)Z1 zPuqLG*e#_qols7zS#}d)1@DU+Z@n6{rRNIafpaqW?9nf~U&lyG93PLHUU4_jVBm7s zxCo#yeK2ddNo3%<&)WTp+GRI%*@m@k#em1!!6kJP@Y6E=#YJ$ij+;(N^0Y?U4 z0;mH=217b>M-M_nnA!3_=>&nuD{cFO59scmOs&nR+E0HE{+`Vl6Bx)3xu+(-Or8zQ zhujN9#!^L9MVv(KW>k%)0|~BP{7DJc1-0*oV)E9gv)*AiltBU&;{0+83v&wq7buIt zD>b8DS>>Sqn}IKbbmK77Qr(L@@JOCltXaPf*7LtTuE&`aw*CG0_z%VZdaUxGYG(g| zMOoe+oooJ~QevR)+&&8p{k!X$h@gpWs{H7iO|}4m_UXd#Huw!h&u^vym${yKQg$Ww z1$F^YcV4Z0q7}Y`-Q=dyQ*U0mK;? z;Yn=a%v1OHh)5{KFn;4)50IO# zkV%0$APcw64E4LTPdk+N>KFiPNaj`>VJCcUfv*Ch^%P&`<^(w+&$+#g1Z%zd9atN; z69A0jqP}0P?Qa7sEB-r>30MwQSrDQ}?|#NO7%-GDM+Vz`{4GwsVytr+xPh1m?(*x}$qKnrw&AH~U)cG8Fuf^V? z^zg}hjRt{d!wzhQRy_u}TL4Twt@ptwp-#|TZU}9JtLn?nPJOg>uNTw$8(?@Ll6@jc z(ZejsJ65H`bJM%GdfxVvMW$a##U9%daSgS%N7=_%T{~+LDbxsHN`=YCa{$7J*(+=) zA%{KAV0ol{CWzn1>ZDi4# z+^Sfn$3X+y>CRc_bdvi=p>`-rr{D3YhvrOk*yyunQxVVyU)06-h9jBo+!I7gVGNT@ zCfR)!3P}->(|ELMhxSfcvW`TOB9xWh|5g2PcM=NL2w((Uja?tcTNN^sNZKRP$1GDi z%F8=EIojBBdwaY$2ZS7W{08Y(-l~Xxso?do`rg`k<^)`NhqxT%AmClz@J<&hz>}gq z*r*>9mz<_09nIImt}JhKC8xA_zQt=mQWI*eDT1$}G;YjQ@YSQt(|dOAesn8g;kci3 z0_bY`jJe>>{Zt@jZeZi|L)@HF>Tm% zDapZE9_@#foW-pYiI%~NW$HbGlSZ$cnoP8iPvOTWzLnm@$^3yh&}YSyR`)xP)O*zR z)8a3(-Fy48nW=dlhn>dBMAxyhlpt3%MGU7&wqyyX4J)FNNsnc>riqD3&2-@qOr|a} z(nGu6U!T-R(2TlpwF+slT)>(}xe)zai{*b#%IoOfu5PEctnAAo^*>qeu=4yu*=9e`@4v=q`F-Jg(SB`pXms_ zsGcXmEKXOKj+BUHqE5Vf{V-*NL2pDUj_`r?T@@0=%Hmp%8oYkro#hkHeeWmJsA&`Y z6Ax4PEu(QheYJT#d;&3HdXh=Y$YyGIC50483cT8j)k@QW8ctg^n_$oAKS|vxIXu<< zOnU;Z<-K@$|Jur_QP+k0^fi&M>Mg&+kJkjWhGE1(Z->UwWy_}(E6;3~FtSA(qHD2= zXaPb#ZfQZ98=;x{^AE&l2(1Bi<8?}{{NUtc;!4nDl;`QiKAoP41o;&*^VHKQ{K%0n z6M^hkdokz2MHeIUOMXBpAqn-Y|x_F~9hJu zkx|(IoCn^bHgmL~zC=-~uPn&2gtsNz&A!+hT66&pbFb&@c`I{*={3d7Z{{4M=|WaGIQQV5^W`!#DD!ow=lEVt`+Q55c?B|PqkZ6ofSF0L(a)DZ|HaKd_IAb?Vugkgr=-pR@-|XFO7EK zU^Hfy(N4+naDV$YOl`D1)hKDuViqlHqLrqoY*nUX;2U<`QB!O5ZeH;+&(h@CZf%@_ zoTfpvsIkMc@&gR7LbRs1$5icaVd*qey;DQS)`YIuOw9KXsz%>^JN}Zc8}`dm(?re7 z;mKIb(CZez+15ggM7+qU$wLzRyC_TTYV$_jVxx=(Ztv{7##0!KL6Iicuu(y8 z0?K)$Zt`3SQ5RiW)+HrkxO1y}Fy{(cl6c#F+$ePC8=E}ohDfTYXNda|x&dvo{N92i zaDc8O%gb#Ww(UJ)o1Tzb@Y!G#x;yOWS9hI%eM~#;y^_a??EYWJ=6+U0I}BeRWK3>W zJN=ywl>G4=Yu}8DSF1;;N0Tc~3UxzEEr})ugMjI6OfxbkA*0+|RAvh0Dmw3PN-5m#%^wq;SNdi0kl-k83 z;W+xr*gF2EQOz!tp+%uro(haj%a?rVdjWKQVv;8dE8(w(h^`xbIBXK*eTiPjs&HPA z)r~z|cA2m$uUMYi?_JcA-78!wX5M|7SLEa%yP}uYEZRV?Q`G}@F)FRgTHXCaA)8&z zXN?yMKByddOU+Ho`sij0Y=6duT9kg8z)dx)jYsj?@{L03nl<_ZZ-}YMHFb;K<*Alo zy0FKBL8P2sCF^3#x}7FZ*AkbcA-Gmi8}p4~_|oX5KRw-Lc|b@n-J zPkviJO_aaPTLF`2yo}Bqjx!h3{bSLZlBB5YVU#w1oxE4lYMz=QXrW(rZbdyIts_i* z6&RPj6)oRfL9IixB9{1%O35tLE2h|rqX#dW>j< zobJq!jiTsg@p$Db>rbn&81D#sFG-!Zs2p`;Mz=O|naZGcv|yZv+e9QHXhYa9Yh7D_ zC1<7LL!O$x!HE0>m%KG!cD2Y7PDm!hlD_HX?;;Mbc(M&FUEp?Y_;IM~#rlZ8QrR4A zz0Ctpo(^HdFC5c1kKQ_NC%w!++BF@+4j)6S_F+sgpsk6&Is4L)0vij;YHn}5Ya1nZ z%l_pPlWDj?(GgFnR7;O%d15}zU8YgV0VDLeWn~?CH>EQxde4v0!MZ2zD%tp#-oEMpas>RS;5@4zcvMvt4HGPbYg3?VQZr{&_Xa8W3OSy> z?8O<~H;yQu+DV>@JbFKQqo}wxa!)%xP_#hi1>7BHGSw7F=Q->FbI~1~K}`>$Wgn`I zImo%&xt&2B^wr)8~S_4hbh1{ zcbj40tuFnspR6DvF=o>NaJ$NNdY{{?$x}#ukDY939m;bo`5|32fY*{M-7RP>a810$ zAAYvPv21-<>XBWgL2!T-aku{F%a(Y{zUhSAx5e2joE^F&a?N=kf~{ZXgLIS&#@zsSw&=u={|rcdX-#YaITB8zjQ6Pr;g<2Y(Qw#wj6a5mFRCj z7g?Lbq@g{eRqv+|Y1I2LjU_GBRObD_8`BH?{TB26d{q@MUz5*Yf}OGnw<<1U5y@5l z3Vhv#L&TSi%+1f48S|a>3xtpaIhVYa*JcNTCBYGN#}er)$uo&2Gques%{H5-7x z4CQb+9#!w~=91$8YuX7D?jkKwEPUNcKvr4WzsSuIVFN9=)S=Pu?4T4Zg_oA6G+CdM zCe_rIIAVJg#=^xcH26l!m8h^Z6B+mt0PNzt6R;S}@xkG(WwHM&Q#6w=JsEy`u}R2;rPPYhp-VM#VL@xDgzb{a@{YcEu<!H}wKY#A2l_-kOwno9IUWDx{fsa1L`mAh*H__OY{`0+M^7`$KZ1WR z$eb4-z03(6`I_Rv<76lP@~g)&3o#qx<{|%zHc)c3^ey@o)MLjhTI&OTptf=J5+>*A6*`1RJ{wb!6u+yy?jGJ5BkZOd_r>z2il z3-rfAKbcll^UUzMd!N4upX7zv!?zc1EjrR%M(4J`l+}low*7;ulvh4ICEaB{6dX^80@t+F&v1Z?}IS<&zUkes#`_}A_mAnMPTKMJ1AqU*FUR@!BRZ4u%JkqE zoiQ>FxO)eaf^i|7=?9ZTK>R>w%+?86*9v3m>!{ymxru{zti+5h z@`0IwTYexJ&C~R{7j9*Bkn{2jKi0RAopc7zN4QlQk-_i4!Vetgze`$}8yZ`oY~sVo=# zmgg3gXAi}!m?@G0^yTZY7jD~dAc~Ki{KC+Iq%;gyGHk{Gl@xHO_tTq3LHOqc)^pbb zM0kE!b$k?zz|W2l60<=TS;58bn1vbMHzG zq;cg^(vE^lVQ7WFJl}E|{OMLFJ(sVQg4*{94N~$AGvRVS{`@i)`;o7RSB0x;=byK5xQ<3xqV9(G2shEBokBLsZ^}(`n>ht)NlC`A)BX-IxcSa zx|c;Lq(qKyy|lIPXAa?u6UL@A$pXiiZ;bo#m&_7-$<^o4x+>S?r%O!}QhAHJ z0|*N|$qCjyeWJ$v_&Iytiti>-Mi~3EeaT(c($2~NZL*|)RMiqxbDW%WZ+5ZoA!lOY z@qS`%obKh~9QHqq?H&b0$uPQXdVp*uNq;OEC ze_g0#Q;QjTW6X4|c+d*G9$;o2y$E6*+qs)-08# z7in|f3z~<|V(dYIl+)bx0gIxwfd^*ZksK+{%b!m$it33w8kiA{0g>_RFXw2iSRSY! zmq~w|i|*?$upq-ri2I^`P$LJHl8Z$DTil;s%ksQS zvPq~9NBHq>M3YyT@iBzXbtC<-&nu}9QL0(`mO?}Oqwy&_q^tWr7Um45qNM|WAh-uv z^-}=y|o`utE39FJRd!GSzD zn}5uUJZE~Hx6W=pcHeI+iMN#d$ii1yD`yVLHM&c7qtVL)&f7L)DLX%wd-8to--fT9 zkZF6CxSrTqb1~ZdV)&70D~{}QJ4^~u1+ty*0ipX3tR|)2# zNw_3Q_)uTvn9s<@1KAgAjcKB7IR#ZO`C|;}Y#z{!-?}~ma4UvS$Nb2~!b?a&4xv@OU{DNXpHDVX8$ncQ0Ad`TwyZHMZ@&jhwVc> z@~sz-25Gq#tHT0W^AEw;T>^UngbnEK?{BSfueFEB zTgoJXo~UKC zY@-7tUUR)@ILV@2Xx;jZ#^CyfMLoKx9=oNyz4hSD5r~k~4;4W#Dp}K{7X+W%G@`q+ zcyKDK!romsnxkS>HiEP|yLH~tjKDvTs6UXvh98-XthqBJorW=$UDpsu&%@n1Q-LH`fRzA)Df<~0N;eP z$!-|t?@KNKOo~}r#l)oMfI>UFV74_CAZ$+Vr!B+%oxfB4NAAl~Kfq%D%1x6}i_tsX{8!`Op2AP#sS&D*(??p$Mc)AK8N-Xs)tcCENnq!ZIM$r`? zoMhvn!nr(sDPK_mf@2cDB<>qc8*Vvwx_jlzLI@`<0(zotcE)D(ggO&iJKzzy&qoNS0z++ z!T|+Alz2fXsvl;dD;e}d{IY?QzNQRfI)9|P?#GwGQuCb>wSewvC?!wFJwtN(2|B+a za6aJ@qZHYpL;t2+ebFd3y(n4q*qA|hjgoy}zMrm}6a3QZVpf&&^T(wIP*SXyGfcU# z{8m>bUANL1`zjnA^W}wP+vd7L6Ay%|occ>mO;JrIr7mqgl^_wRxeEFF0*D?1C%~Va zSSX-%dQt2g?>SMJn#pvOn0BLnE$Mst^k%r9gEJ?=emzvk((-eoWjJr5p9^aHHjj*g z{ntqL&t`tFK3$P2YOB2#FX+Zk)D~TZ@m{UIZ(ua!+JRn00H@!=b=5#qa={%Z6>kz{ zmDAnUZ-ikD+$P|QL^VqNpp?11XtCup?UnCho8cj(r}%M^Uj~_#SsYzqFc9$gleB<0J~#5z*{z@c)npQ*g@ZK|IO0 zgArVM;+{}#RmR_}KOMz_Av6!qd@mye2O{lq@2o~E%orTuP3IOhF+EF`j~O1mO=9EW z*Lhg!eOmaf-O2YorP(4DUKp>pY%N-DY(23eMEEyP!f3Pht$p%HdCqkw&GJ~uB*FOv zqn-*VZ)Kr4v;}QcKu40Q7JTTme=Xy4;`kxas(UEa>Lljul|s0SMsi?S>xBnJrzm%& z7Vh1mZPr46PDPvdJP+5-Usc=1dFQAtzo|HzY60uj$sX75iHL9ylP0j$I|!&&W|5Hy z!RYJ8t!oKZez4}w1ZB*$2ijt8;)IUkTl&`#1{(&2pGhK1xLaTDk>*s!)}ixuUeXD9GE0gK`Jr`TD!s}($Sjx)*zUB zgjiC%VN&GA+D^Ev;c!mzXF6N*Rs5hUJK?_Lm*Lj4mS#PQq7H(yQd6kZ{GHN5fHvTh znOmqAWs3zh=+%X{WFr#NN*DxGj{;~ zv`^`Vzqn2BxPZB)@uhPP-X((FFd6jkC$K?95mb#*k=_bZIo}%|gj4SM6RT%()iD9@ zoAO6KozdRQe)G`m!Hp7EM!`}FO2aNSLlmcP^W~T*Osb&K*iKe}Q06b*Es?T$h2GoV zGuKIpVAy8^%^x*zWqG%|5;NBZnVujIkGNW-7y{ z_;1k1Wk0g#Q#u`UO~}tOIZqHK_#w)$D?i^5rnrEKAG4Ve3}#5j-B}cHu@V1pj^WxBfr(&}HmD z&U2O%qCH`{_2B9Wt{($eKtzuxCC_#_V_)_L1ok1wXkl-4Rs~#gG`WN=!_0ljWxx_Y zODh8l=-V<>-hO0%3 zblq6|eM1ze;^n6`=()EG5|x2}=lYM>r`cUj{zq(9a82suZFZFHd4gh6m@gE_sse5` z4;64T9^8O#lho_#1XWp`!EB%iC)W$&n&La6^J23DwRPD3cltIUIT&`Knv}^G6Y z`>VO#mb2A7dvkOTc4o6Nx)%MC zSDR8628GJt4Z^M)YvazVZ#a#vk=YQ^ymTaqjrc$-2L`xkjsi-%4cYBeAnN@vyT`+Q z#wSMXux%43;iU}CDO|YgQW7jWTW3$o8qnh~xK1SH+V8qw3Q-;|rcb_hkX#aU>D zSDVDWg}s^Gx~Bn!`gmiJV{lD`+jszZ%*H|RRdTHtDQLpLi^M{LQAW>Zs5)d?e`886 zCvXt#JcN5ru_Ab$&s|Hm62DS|U8vs84`@}&VNJ1jA^XD5U>5t(6?^>!)|94Up9nX! zg^+5l$Hsj*Ws)@SfG(_KS(zi=vUwn(baX@h&F+a{UzbEgMJ)`*;?t#sXyr*4_k+7r znoW2dc~1`oL~^vfdH8765c0Z(%0h|D` zR8*otvM@s`)gCh|)p1UZ-2-<#j^)j0-;Nd zQAUEgBfxOIO|4S=nTCm-jBVI)1_#5zKwp2Z4E2Yc161=Cf0?a8aI#uam%E8CcpcRH zP$wvXKvL6HY(f1dA73mu;VmHH;j2lpKZEvHI_a{h6Ldnpt~3Qxi-*t54TIXsBc)J^ zZS2p3+F_obD?nbxCf)u6GV$M;M{&MIc^?OupXeWHA3n1xryruhrK_}-gOn?qPQ6&? zAot0(nPN(*i~k{GkVI)T2znJ6k4uZkW!RnEG5iwA^J{C{G4UG@ilo#6U!R)}xA#l( zxo;~cjFJqy;K;ux+8H-Vq&H>xf%&Di)(R9jb= zV9Op=H1OCe*N*G+inb3~kTq9yBx>DwF*Ul$r%{1tJoO^2?ODP^Zckd>RAO%Ssm%D?J&QRQ10CI_0dfedb780)Sgg0FppojC=`EP6Sm3wxsy-mgFVk_ zjAYi>OV_C?x!B_swH0Ax<@{LpTNQ*yoU0A5l$>K$5-0`@TYLB$dKNq@Em3o>;y8q> z;VygF{kWKlY7Qb!FXx8G4NSHCMpAq$=tO~h{_6DWvMS0V4d@$jrq=l0I-hd7CjM=6 z!Gh>+6@I;aWJL9L3Rkd2Wp+@q%_Hzw?%Cc8`7KVr)fsolCrlzu$|)2GJ**Vlk7+JvDHT z?*Zv@V_2MsT?U=lczh9Bl1fTINLRUw4n-$!Bp&8>y1X(p*fqXY(_minn&6Sl^h$)5 zW8o*R<}&qnC1qHK;c~233uj#F`cdh4i-qlbhcj)AkA8Yh?fbZiM+tt-P<(hTSv+=( z47y`CC-C{=6^iei%3rC89rfoIc$jxOrMggyh_UTNgQj-x-%#vtNa&56YAa7y=EY9t z3En#Uu6pi~^{$$ws;2cv4I0v4hAJ;TLhcuacbjUlL|)L5*pyHn?!nTbzxH15d$Yr8 zPjEJe4Yyh$@v$9!w-=LD);9J$y^>1@-wyjKFm=DC5ET{>AA}#l|BJR<9esZ42^D~X93;n%g zSfqQ4k(@K8XY<1O=HZxwm-pd6mai#$OGzyQNlC1!!rFw7gXr8PkAtEB35n)l`&RqR zaTSOnrFb6OL6(nw*H;xzBcs!cA$H`Gc2{F4Vk$K^W)9hcb5@|sm)Qd57^JDi2IMw$ z9@(BtNQnP>&<$A5;l3bdM0oxW$kG=G_OcA3@ZAk*9AJFpJhEDVpV^jU(u7V#J^+XNY}**uO9w*wN;OZXru>rt92Ep;slXSfc!ujhN2*u~F*(G_7prcz5A!AOPAn#hv|W)9=JHx(}C{m%q$|3bz7 zPy7bhh$Tmae(}r${G$P~l^PbBWxOcFAHbSBj9OJ7uDpc)mEWcw z0ySDohS-d=r9Qm_gk7}s=r-tOKZSO|i+#BV2gw!1{a zpvjZAN!$kT>2nGcQ6Z8buNnk%Zg=C9CyFZ;Hm_{7xmA7f>{yNT!e%=^0CgbQQinLH zHt+2g!O|gY#rQ2X`CiH|!)bKS@rF(H>z?kY>C+~)&mQ+)cK;&V+I3TbQ%4k4HpBCl z?M?O|XVe9yvoY7^i(Xfc86-b@C%H-GkkqtZX}OST;H~S1f~g%&qoR>m2_01d76V}Ytk?%Bb#8mjW>A0Ej znSHl4=zi)zc2k2?iywQm$Rx>ep?BrXcm-CQC~val@9Aj=c!WWUo80nAJq7i&GB(){o#}r-vho|adVqoxV}fp zk#_-u*Q`VFRM4pZAfjO8-fUggoJ6KigkeSeIvcrJA31vCTU?jWsg4Vg`8??YbWiB2 zCJ)rY&SYEPOjQ2#KzZ(ld9U)d{mA7JvxEFu2TKW9KqjpY+W8J;aJfw1wW6e{nB24iB$MlH~Au{%p(7BX~ZR zBmBK|Vo=2zX>CAC;acv6vHTjU(Sr5_7_Ox~#iSyB#D+-`BmHVVudr=;NW?vAzu`G! zv!k)ruX9xZmM|CY)#mz3#oE!`GhjR;>1JLV@_^kdTD`C}!K|aAyd?9Qte}?NpGV@GadQB z%r;ln?PuTO%6(hJHUq#3bm52))SRY#A7Rg}g;6Y4{;OzM?Ga*q^KSIrr=nKZlO0UU zb*m$EQY*{j0QV(_ttM^c!&9X1HflHKx+!dUrxQoVUSSLHd+1`u{2hR^tBcg=G98RN zNWbaK$I_OcJ)=)f+kMwTh}GgW%`zbRyI`%bHQEz3;W$hhEn(Lqop52Xj6 z2j7H#va#}JVsetO8`2&5I3Y2H3tO@8h4Ib9p(2(lo&ALF;zv@tF3!-ndWr71T%R-P zK$Fl{5165qFHme;C042$Wv}zL+hC+ijODelc%Ad3FRFSf0TEZA7BGQRQWA|HE&A~( zmq(Ua%iQpC9KDJ9w}o5^9$VhFlmf9{{(xQ_%pzXVX+&>vakEnJhF$f!`^2UDzMn5x zNZ51avj$>sLa;v0G{ZgbRoJ8wgAHRFA3tZD<$DAufKcl+QEG5C7^>RXn9Anf8|~86 zXsF)wDQQ?k3_kGv@O@oUe*4^q-_YQYF7KL8AcjWEbt?m^;Ru}>o}3OLo{FYf{s9sc z_`?oV$WKZpKr)C??GL!tR>rAE-+qGXlqiV#&;LXwPdIYyI#3$#Hmjb;6^|s%cB&ad!BzHyzPj41E@W2obMa%nBb5JOoSjfJZssK?_EKB$#|Qs%2}^fXF18q zKO`y#VWazj4~-#;E4#9GU)iUO&6nb%$SrBNQ}R9HH9`+5I;)Z>Z7+g&~yS0)%?I`5?(g(=((cjsh#Wb4(<2e@_S zn8bi5e)>{XX8wiuC8bRKZ46e|-33sb<~1>mvvmjrOs!M8ihNTxn903&*v&oeQu^2N z-PMGu?e79IA1IrLFIv&Q4J}-uEgPrlti7`+r?U=Lre>p8H$Ij;||S6zd3*gzvEiha_T#8c2d zlz$S>bp=Zrq)N6@O%Cq>vE&%@+%jNWn=q}S1`es91%XNYHIoe*0I}KJjt?d{XRsbK zx$g*qs5H18RL@OCx5N@q6~6>#hGOT|;LVjp{1U)Z%W<+>hX)3TH|E{}On>%j=-V#g z!apJiTsT5>t@Vb|=SN6X+s zz%p<%$UnLr5Z~9iPPLG4NLc*0%Wbfeda>KBNT8Q>me{LCTK-=BE?xqHE~w7TjZa>5}Y-(0Ju;o|#XLvQDRS4oCy z!Ul^-`^tE91R52WNsrbj3@=zM&hg`Owg>S2UQGyTD~BRl;skFAAMrf$z6@xgQ{D%9 zE!`H}o9mf1InVi|oFTcel|%s*^v0++K} zE4iL_H*9S2zE!Qgr3ZZ(JEf`GRtJ^vlkD4%@4Zz+dN-)us-YMe4mMIr6|YfGz}64- zmkDkc_G8pclO}{5vk*QN&Q&%6xK87ZR*lyeobhgYLkaHzn8(#HdW3ImcKm&Q6}k!%&WN4@9mNMdL`+w7*n+md7(QC7@{T%4 zO;ob=8S29cSI(xS#YQ26&Lb8hTy1=DUASU{Fekj|z&8)3mP&i8+UUVK^_NCNekt#j z*k5!>@q)z(&2W69%L0#URk+1X&488|Djf`0G#SQCvXUl1@eHkNegT! zaPB1RLwX{*n$6@;?D?`iL-*vcN9*8QzqDm5x_zK(&3c7izSAYMbhKw4lV{9AH{lP0+&&^FTgr*eB*{4 z&ck0_-OHf4xkVG^R7DArf6TcW4KM5{x!n@bRhxxn_Pa#v<1D^_LAT-W`Sfe4O<8^x1w(?ZxbmvBs6?52-wRV8 zQ9KOoM6PXzf%;LKyW{jXUnOhYS*Lr-7{>O9#CG?8l z#DBRF<6q>#@$hlkod+DMi0jiA?Vr_Nx4p*K;(sm_X_zSB>1fim?~ZhF5s7=~;5pi@ zTRC^H*h)$(UYdKs$8$(cns+k3GM7(EqYV_NovoZ_e)+8yL0e8~s8?UP9YQ6cM19MH zqI+%Xvyn@>W8)do*Ec!rx#t&j0xFQs4IsU&JKkKT6#_nvpl(P0W}*LJ+5k{#lenwmyeXS3sW z&YHaT&;3kyPNnT6-bKISrg;v<0w*&sj)7A5Mfv;V;?p|uv)ay-ZU-#dT`fDv8p1#6` z8v)(DirFztLe_QL22ASDI2DP%Dj<1%TK9uCiUkFGhVAVVuVI}q^P>xf%KMKi8m*V& zs~UbZp@LGf{V;Y#vl)r{vy;x!EDMkB|5x=aQEY*^Xn?cegu&w6bI!=SsjQFst;y%f zH#@6t(J7GSq&TmwTRvf8u!^E5{vyMBc?VU;gK*apC9)TKNV1uGybDa*Q*|$J%OKtB z9B`aKH8&!U2zz26V1S(8*6rMu;MVjTe9thM&so&1mX_M1A0!31zoeTFJ@G#Y{+u&C zQf#+8<-Za05ywd6Xag2qHfIn-5Nv5!%qjpx!4!K28fSy zE(Zb6Of%uLV2>nt(4U7!oPP5(rJe@lWW5Bn?mvi?>RzO$g1lT`VC54pp3~|}g*VEQ zH-XQW!S^(?tRRz{*|c>rfBMb%Z|JM6-gF4(NoQt$fk#8A8(TXdOiu&O4kV^uj(1n& zU-|>&73t(Rk(wm$xR`&vcn8dNjUGw)Tkx4XKpI^9XK!hR9)A40w_s|(81#_KmGD~{70csJhyS_*4e`-R1@UY;s9O{i2c)S zWl%VmSkhh*3VCKR%NjZzA~i#2?^>#m@N>$T)9-lA&^zDn^mb8n{*pqcjL?&J$R2Cm z`;8(uqB0Fo1}J-_?UB$?552SkkqZCXuD)KAXTDC^UJ*sQ z<+J%<7W7hVHpGf_BlSIfT&Fec5K-SLhBcavZX5d7x!-ofmS#32iWg=l`P465m%E%b z^zrA6%HUbYu;+KU$3$sM$ThYeS8NgV5H`u+YmPxO4ARL*hc>6Lv5OLRF zO$)l$_6vlBHWOMuTXC_TC=6p)Kh!? zzSqsxMvFC9Mo`3#^%*|*Y2|CGsE^=}LOq!FDQdL8zezvYiYH`nS?xwuAo<~YH4|me8p2u00lbuD=#NM$~_cHGb z$ds97Be-v4OCGk*XyJZ<{l{ zR-rTyDJO^jR~|I-x4iyr?NdEh{%mD}6X0aqc(Q#Z_yNQLIj~-na2eS1{<%P6xIFAQ z7;f1D%@CD5oLkatnPvq}WwH}lb+T7TeI$e#Q^;?tNo)~zHeK?Y$HAU|gb5_{&+f9D zetGhDcZpoUw;(^}Md?@wN5tZlp}^(t$iOeSaB%bKH}1in>qmsm|3|u=bn5TVC(?~r z>OW&V5N-TZx{)-G(qM=2hinAjw&_=P1v4Uwmahb{rJ8u-$=|;aPZ8u1oFI=NlQ+u2 z1d#w-?ZjeWHmtyG07=yXd={|%H$FkTC$kGNzj2rI`~iO%0K&;9lT?Z&$S;cj8*dC! z`{EfG(+uAw2X37gm!m78o77$#@~O}rA5z1|jWq5{Xc_5l3pJGd(xNXGyFmj4|yMk=!E zeJ%+1hCVH{_pkH^8T086C^INDj$40BKBU!#FEo9wbc^u{ukPjBWsDYI;o5^x!@IwB zf3~|J>{Ug&eofDN_T6AG#gcl1hBp@QSMgVOKQ<41*y#v`0jLw&1TG$8(e*7QWbreW$ ze3}Ow5>vt>+Qj_SXH@_lDJE>V1Gf$T0V0HjXZ=|0AZii6O#*{Nn3n8|y$p)CFH#y~ zKz>KxEhz3o$GlGl(HpmZIN;5A#M;zOp#8h=cfI2e?_bXiKYECaafO$NHiRT?1mcHo zVdV*&8DYy(Y=Q6}lnQiVOfu0UkPCDDKHFHxnJ+%(?T6Z=x*srX9GmIKM4({nVLL}L zME!*&AZCeBIo1Ix$Bv&>SsY7Zw8_nC{UgB9{RJW=rnaR+n>6p^+4mu*9!L`!IrZQs z=oK%h6*FS~yH*SzjR=uu`QoDku(G~zq=A;8`_>*ahaef{d)QzH4xozu0NE%F)L!&p zcd~-XF#7`Ln*GB^Fh>-{gh@+U=nT`R{o-gFF3edyo_aU^5kkq;Y@duJp zk0X$4|HcIwi*3h;#1Zj6M0TtzkbQ3`eF)?hc$tLPsKkF}a9a)}WDWofu}7EPZw){_ z!l+t+cqUEF8lX*5L!$#2HEwL{#bPfWfS@TO(TFxwL>|0!FaQ;ioxH`nMKX)?L0|Fi z+N2F(8<~HWMhswA`OvY{a^mmmKRqoLaK6h{-Eqs+Z$N+X$Aoc< zPW}bY|MQucJK6CGXcXQrN`M@?crwKLbkGIBBfL0s+f2Z;KYLQigR|x|hoWe*-YNa= z`3pv*ng;^s7hbmLv8rp6_0}&Z>S?%AO*U5d6kfa5W-QXhbcfp7MpN;JAB!SHJhSgy zvbiVh$+q1~fH}hVR$=ftLNCDks;Pohgi;+Y$xpRn`;RKLHsq@yRWJ}ReWfQ8ONl`9 zl5fPGm&6rZtlcFJ;eITho()M81G-L$>wPtEo&EmRN%6Wbk-BX@Dk7GERIk#9OrEq2U6|MVfu< z@As|x_pZU05^?gy|KlZ_>jA*q&|dy;TOc>ApF3uf1S!bBq2q!W%a-Kd5Em#kV;uyD zU0_>7XG=Zd3ls7n;rln_kU3tu_#5hke|u30@BRfKI+=wEz1T&9-jXeF$ieg4kGB7( zD_Fe(GC?NOzZ=6D^B?f;f8V_;qZ=XZ6G%Lv_5A|b2)yf2BA()3lLzLBV2J&jzQ4jf z3UD`9_oCVei4|AHl*^@Q_si^Ojte1v;QO5eUR3=V#H|pp)=X7YAGjP5sFX;2z7d2z z?|(;z`{8ns9rQr>7f^;IPb!ajiT;n*7%eZW2fQ}DPYp^W@%gSmM8fd{JdqSqK@pVR zw=@6b!zC=L4~+7_o%aNYc@h%fE2PvWi+S?*D!;-w2QoiG&ZedJg^(!_&4#d)0m@uO zr_fGK@+pgX;8;`yI^r6QCPCBWNzJtoe2N)6$xpE74>}d-)|l(PGoXx)ITTR2+Qi@` zOM=M3=Q9N2razj-8Uj{`^$CraxDbH`ij2TEb;l*t{u_|SyW$`qNG{`(0bS{wJNl*# zowEjzZFa=mic8l?{_JmF5lIl|cunm_Q0Gk0`X7x;jyR$@(YUP_$)6}dO6`gY6fbW` zW)Nvx<(3k5xnjL0`3&13vGytTV~O?Hjt?DU$XW%SmppHpOOCiK;{Br6Or+%HywXD}KByL>6zELYy-$HIQwj0$oaxt#C<>{BH#r zVigUj#h_dMzyx4|$Ri-{RxmlI%4xVH|JZZ)$Zlyvd9u>!OA5ppOCV_qm~(uOLJ8*F zzo^g($0a=16^ImL+e?A2&LDNeKL*&k&zMz6{vjVapk)QB4oK!~9OPt5U{|9)ORffm zVaSF*{w*I1&iZaDK#IurgO4W@IZP0cp?&7$Q}87gNKM=VW^|hTa~{EF)1a>I9Oa`KhA_o%%HhC|Dvs}TbQdxpSpPKz_q=FK_N(N_EAS$6o^_+2!?Nih^}PAI1! z#Q}R`{St3*`k6zUiO<6HEjil@$+|TSoFi3OxzQ*y3AQ@f>@k^1<4w)3UNow{c`xRx z2Hot0P)SL#=N5>^y0}(uTkA`!XPRK9?shj^bL0Njb%%G;#Vw*~^uVvqu+_0s@D`>O z-J4srkz|yG+4a^$EeCMp7q~o!{Bs>eF^*c{_1y26lbJH>QVlMs+if`A9SYw+MY(9k zsW>{*g^=QkR*H4248JNexlZGU$$39ITE8+nJDTKxo1E2En{rx18^Zu>NF&Oar#Q1G zwa(AjIV-!{l8qJ3@T5d=W8HYI22rlU>`EztXf>zI2|wW7Dsq>eQW0==Lekxi#bz^J zkAT612JI^+C(ZN4mvj4dZYRafG01x{X__c)H#im}tCOY0>&&tiDD@(|{2mHr?~z8Y zJEfW#-|va7Bk2yxrMdL_Yd9!+C@HLC7Eat=*hY`77K~v`Jx#moHS%f(YiDMoCJiD| zM^owohbs`nxGs^ZQWwKs$M$3%)2-;(?{$`;dJQ$z2yaLLc{j3P@@YrMM(M&uvoIxt zdsd0pp1ie6l9@_`P3)n&_JfjPRRP?nYk@nnXrpb6;CUY(*kn`P`vuqMa<&^e zqtZ02cIb>Z6Q-^1f>~7fzXsF-n>wQ0%B74IU7~TuhWEZ3E1kJ8aS6Go<7h8u-n^EK zcp6{r;Z`ZpBI45hHv76;CAvOFtP!DU&{Q&av6tWC;NH?kj0Lm(y1O&A&q{ZVz~Q6! zu~9wU39-eu-xFuOH7$}3et4B?@}Z`nhZADa1=iNpyV?_9sik5&97N^jR*tD2(s3|! zs~&3+JBY7y-EMRZwcQH%a_hyLu^P8ddfG;W>Lx=omGc3qp|0ZN1?-ua%h!qI`PE9F zU%RQh9n-KWj%duvd{o-YYLYYcKGW8&4knq)E7_?xZHOpGeUHpSJ7N%(*2gGrguA`U zw6tM!%}CMEV&DCZ2eGq!0-EK@-~I}T_vE^7P5VjaDSv=ibJ{ALhdKAhSYf?zeSoL; zLGGg_L%jwH5#LhIU6Kcw;Scyq$O-5|0>yniWC!>W!HJVbfv%h=T6zEi5_}mD*#j-e zOEh_LK%4YJhfLhJpjQ0t5H5X203VvMdvfLvO#^N}MFrYxG*Y!}Zt^K{L_(h$avdNl z>S$S@-A_RrF*~6ExB3c!B~$+Rj2*Fo0#F483Itw663e*^p#BIHmZTyX7&hEe-AUF< zh}G(f9cVk)tREb=LrC!4AuJ!@2s`4_)&vW-sXqg#U^4l`#gJ2qhiOY77gw<#H3Cu! z!nOT+@v}a1<={X`2B*;@D5+mqxe<_{5VIv0269vtE8#6{9>;sY@ddFRdjK5+9m{G! zLZ4#rbs41kjBHwZ3bZB}X!s1W4iCUi3qsRN^9dU2H*_A5ds2wN&n|(iRBKic5UM@y zF9JC6geC||DyB65L|Gj~gC4QhDbBaxqNacW@FoLT&87p;IV(`;2hs@0$bTgPn?hcW za9KFnviuB7KNjG`gJ*voAiXI5csg@(ioKWmRn^>MCMHdHCX*|nKa_b%1WdHD>kqh0 zG@qjEYV>1s4PFMOnQU4K?H*9wz{KA!QWsYcoHYBCuXJj(Opv}v4Z%V~dF5*C=lTy< z!VI)OKBXWF5oLnfQ6q%6d(@C_OfUhCG$82*zadV5^$lJys2vPQS5%-YpoZ`D0)@9H z3tj*fVG4km&}EbXstXcu_)kd|9)Jh>76?@Y{J(RSi9Xo=Wa$UFVPtTUDe#tc|J~wq z52n!;D3M>|D!|I`($;63ZTL@4R=aPpLVy;I;=Ygk4q6Rb^QOkL=7bQ3a%^n?U}jz- zw%MRFAWJ?JoN0pPmT;?J-$8^04Gh{t|0KvPl(0A{@@~kb<%GysnIiU=f>RPM8YTq} zVS?VRPj@g8HN0UCG&465;U~hO^xX%`RJ){h&JRZ&eD5h{EYQ53duzc#x59lPXDBdV zLdtbR>2oQw#&vfCWE@B;%49LDA1aS4IbNNnlm@l)o(o&%NQrQ+st+as9+$NQHqg(L zrqK^MwqN_HyHSWpy?Ik{#fG+{2jE05EFqH@%AX}RmKU&8K4S8yQ^_8jZzaVNtQs2=3Pv zd~=&cudb|ImJl02M2_CyWa8tpuF}=Dpo&g%h_4*Bx{$J5uZPod3uqcmt*a|-PU$?D zn%(QA(HrqfeqL?T`8p^=G6HqKy7Fte%A$*X6^=2B36!&5S%`Nbe|xpY@@k1BpE8}p zp%{OQU82~6bl#3yL6wSx-RHnp$O%m6*R^Cr9s7W(Qk4s9nO*NO@q!O#OQn=eJKIdd z)sw4+1xwa;YZth-m}PRdoc8p5E+&2AisqN@boDx&Z}dcZPgm44G=uLq&#$2;y2{jP!YhJK=H9QzS!Ds*sH2Fs!@w;g!^HGXRIlGR2ureE{5`T zX)l{2g>>J}zTU6*b(V8#G_b7iJyQ;nT4?wDQDh;NDV9lDFUoqsmgibxAR=&7HZ!l8 zl1G+4AJ^Qwta&X}EjD-QmZ%RI{&X^q`NxIt)9Q6r!#O6;ubNtS+k7{Z`=QPuHNn;N z^V;cyn5Xv^)7x1tTe$>}AckRob;Sj9AD?OPJSv<*n}Ylu$!$c zxtz^q4E~7d*WMA@qd4iT12dC+%@v(z@07MZ27TUgxmu<-IGZIOFHIl-9xp80*)j*C zL!ImXY=#;@ zZrcyX$i}BC&yT9=>u1W@;kazw{WbX})Wk+hny|WBv9kKk7v4$M)#lYmc-g>Zhbw(z zjMO(jsAcv?JeYY{ugli&M+3YV-_J(OMMm|YXm6Hw3l8T?3+J-gGLWXm$)>$BkyK0e zSEka6pD|?MGwN)PH`>V=tBG4EuCML1n_4gME>Dgb2PY7_Us`{SUZtDrbuM@X<8buE z-My&#J+OAx@6wo=!@l+8B`HOQmm&`>pQ?@ykyMF)8#T(^R1;a{vFnRm2)KpR4gS>1 zC+m8Vuku^wy>$b3ee|?}zmkXgB(G7GhC7e3U?=Z(iEZa;O|7XlrmSHDWtCUGwf46= zVwj&L>~ghpWM@Qze+6UY-lVfcA=W)}VL zD;@iN=Pl5E{*VI8l8o>6T>8>Ljk%-V{is$dfue1j>_O3bjmWE{y>{$AkXwz>wh;k!M{3>5L(? zcpicyjY2#IJGXI?&3J``U=sjShb=chOQ9soD|W<{WjBC!@CEQ>`TkzK3toLz`SJOPP2bfNcZ(k(Ga0 zYE4TXT#V!asvF>)sS@tl*21%goC*h*?a3lh$6e3`?j>vtYEYIOM^AL=>$D?{Fn-os z3mk>P9ovAluYLJP`jHYu#@%r|w=0XBJdS{c@X=}pjPj-Y*$ur-J~0D zXm0g>DV@5GJ(uspGf1CS%a}pkx~1>FwD!Rvkwb>Q%s1}%(jqT?%wtvAD2R89p@TD3 z{vHXcyIi*Eg175X$ay>+ju(crG9TaVHl66Ew<)9(r?~?X>at{mSEJn zgKve@ES=b<_~ zNI><~3aPR?rBBPd0GslwHqnCk=3dNzw-$V#7dn~k9|1fy;7!E)WgR4XV+%!R?E=42 zaZa8uYy-gcv1`KBjAVuri+q>Ghq^w}8qah*JsqTZx2id%_P&8C##;FD&D(9F0!*r2 z2KQd`Nhi)*LgGm$U6Ceq1(MMS#r^$4fpQ_Rcg4ZMAnrJyXGCJ7<&@?qPk>$@AK$w21{l8y0^mT%*G5AG!0074z3NHr>A?Q2^q?9Sp_Mbn+)(CG- zMFOHQFiC^r@?(EynE&p$d}DB0IAOks4>V%-Vh(tIL$nGMW#Z(2uX`eTTJd3AJDc=A zC@90R{2)%q&zrRKF1K*F$UQyHYOXx-1i_#{ECQgut`$55f1aw+3)+CQvAa&hCZ9BL z?vh)VU=U8Awpz7KH!;pKB?}Le?{qL6-Ai_+u|=mXM=n$gR%KybyrKJV)&+CDbYc^1 zP9w-i8mbb?qmVv6L;K!v+w||IDB|Rn*^-?cv zYKC>%=snM&+k5eP?a>_&ZRTt{a&2ja6^J*F=su(W8W)0%N|m;5V&gj#ggueD~Ah=&8JFFLr%F^$c9;#?Pg%U z9J;}=M%QX{5^1vToVs5z9zBFVjrDb2^6`B#Elw1vF2IxUk_0!-Wlo8!!xd#E460|s zN?}}F*qrpEbNQzB90p=X8WjmZxcE5#sUlO1$z81n9p#9VZK}5*d{8%xL*~;MB71yIKv93bP(8@eV~`vuzn> zl5`_bHhxOQ0TzyoTfcI(E=JibV!|&yq=c)In}eNwh&sE#(W>anBKy1wq0U!`&{Vo;Jlyo%Yo(S?02fz36eRWYLC7 zPe9q=O;Uao%P2txY#k4NnBL(FkxW(_#*Q ze*3CTDn2M24&>y#`HeJ!e2HcMr^>kSM;PxDgfVnK8Hi7dySr>_+T?7rwQt07`({8r z(^~ot0D~C^9Ez*GkT>i9148GkiMf&!XSDLwgxMu2gH1O+^5^YWLfbE+iXHQ+j>=YA z;xAvUaO}l9l^y509&a9b0jkg>DU00!Gmr(-f^FGC0WE2_03TNL1SvAVp=l76vH*D6 zneo3NjZ*_~2a~hFP@gLWlQY-_qESzO$X9~l0b?c_M0d`s@Z*zCzeB8Ec+BT#O%Ymn z!-ix<71H^0rSGWV%Z|X%fpu3rceLTL=tTuD^@pAC|8gJfCx1f$zabX*-;YuJABV!= zviY!9OGun6GS<^zj_#_g;pMY_xsGPd{RL~S!5Gw{4>5P=@!UQ^!TOPB{QX|Yt+50H zgNCmLUi7y%pzS_S7K4q!_FK=*18oQ_iQ412y%Xbsj9^q8Mt82_7a*GR5Aa)u{wEsX z?6wJDzFc4 z_+f`_gU5Q0(4y8!Z~iD6$kGWITX!a80nXqboz)!v13nHCufm^#A7(T=kAX%6Cen!^ zc4Aa9yeAk1S||1yQDDx$%!2#>G7Dgef1?rq4Mxm+!rlXFpaBZ7Jcv+}qd?4tz}rJW z$^HBXBo|Nw?QIb9zy4r4E70=Fd3YRAPEG?8lUz2y|AeRqRRf~b6dV8_IOBT3!tmX)oQ(>gU)wct^L{;aBC4RKkK z3ZLK?{u{4F-FU6-=}ENi{M?I@E zR%+K&Dvv(;x>aQN<+76Tk_>7__qXG3;6>K##h&4uIH|Pnjn9pv$lz!J)klAeX zu$cPv=1W%Q;anWoSaI%`TOEUyu3nHGhU9{g@^1*kfL7e`KJ)9ZO&eXL?wFvM<4jJJ zAZssXt*Q!Blv<$kcnDTp>CiW1s5v=N>{^u+r1L>6nSWKRp{g!o7X9tbT-pV?&pql_ zj@L{E&U|}k`RUGg$|(gg&Tn%}+$qJ5IiAflxuZ}M5c3hrM zo!M^N_KtAYC|nt^`w(6d*=qLH;e&~m2Zu2A`2FJdNqXCbbhpoC(5W(LVQ=_jMnu0Go5sm;H*D%uN(8 zaC8WfEGWYmNh1b@yoU^QN3(sq#t<5q++LP@s3z8xS&b@+oXJFXnE1gS))QyGRyn;~ z+$qp#o4F#wdC3dyaE8MPV_6zN|CfKzdq@2w{s%LkQ8aYb%=XeI^vEDllwp>qp*;*O z1Ph8gs4@=d`iYJ)FEcRip7z#?HwPK73oiE_O~P(w+9LpQpOQ-4hbWkKpZ(LheuF8`H7}J(SiNXti#i;+pT^V&P7NCCDSmxnIed_Z%=7ZX5t&sAXEfdoUpP4yo z)X}Bdr)I_$D?EWdmOg}kDQ&Dytj218383DBIbSu1w(+!^Ql(PX`NWHx7}X=);5^mI zUeGHoR_c1s3tJo{#wCp@)@?chMaFP->HUg27Za~^*EbCNH@v1Y)*lWga)dE`i&iPV zn$m>N+y&5#Z=3vteWQ?l#84u=8wzIg?u>;t9s%o%`{FFHD_|!s%upK zr$!-s`wVpbh1R`D_>06~SqhoEbKhiZVlKZzz(w3nmZP>yq3 zjBV{(i(O?5j?Lh}Wl?~OS+4c-SM^S|E_x=baSvu~@&WkIr17O=gZ$xt+)o7f?TVw} zqYfZLfALSND7MkjDrV0(7f)H` zidW1aQ=coA<1vpWS~I4175?HRj%ieEIpb$N9#xg9kl4 z)pb{R1qyyXi@i6Go{^wS6jZ&}fHe(PPoqPsy96i?x@ycGPiRQ%Vofj2n5+iTBTOb* ztFwl+?zgG+@gY~zn1bBCnRGsimcAEHT8&D)fTFvodb^|FPB26|;r15sz&wYu)C3c6 zz80FlwN^jsJ?8y_?pg5$2ZPA-jl&L_)7y<{j~lP#H>f9gVlbs#mpmd%ZCDMKqywrO z?`<+~3sG%;Z_B|}8w$%P2|QJ%sEx9T>8_2PUX*h2OuaonWzG@SK#MW1=*VWBef;@u zZHu3#w-(lUTS&joihfy{FF$~O_Tb^QD9Mp{DSi*N_bGazxfiAp74aogRZFhZ5qZhw zNl$Ayc^FvuzL@;=Q+6SHw$HLGMEL;WYyG-EaK=8&%EL4LQMiG(>Px5Y$Lcp&y$6l- zVz{_8ejIQx^KVZ}Iw@Tg^TsUQ8jLITGm^8{M;P++7`WJDT#{SFQvDh3NpqvPcy;6W z8X9uytY98IFMNKMukfvLeZa{$+LKc9s?r!0nJgoP3V129TN-p|DnQQ}%3GS&81}U?A zu4@jIird{2+g~Oo6Z&;gAJ5K!yZT$1zE z+;ST#uTu2InK!SRzRtZU*^JVu9$gojuN$K4^Qw;)eQl;8N_O#;FWuFM>7{-HL#AiP z>5f;~trpV-jkddz0vm@s^lEOtin~hlbW2x#BA=_Dxgpmz{Op(>tS&Ea8>1`|;gN(b znqm{3PhLo}&xtd=l>f;*P=55fm(d=NxZ1EuKT(prkVGvVy75X+VeWB0ahH{4Og`o_ z1CgqBmP5U+LrN~ck(zy8vA*tCy@IMrmjk|u9c7I^&EZH-4%T}kSU+QSS@8Rt%<8hgP8Xr@2j zDWnn2oz}+kP_B1hf^|ytI=DroidL#I6nBd!H9GfGg)8%oVDoX3o86KV%WT1^l6+oc zXXy|ITKDV~tHoH0{p2{D;zafuE;Gu@qMd)j?q*?Y$+mG1r`PCqF#FFtHp zXTdN+ePUP}RH6^K2u;08V6a@zZko8Kmy4_7ABqv3DwaexuGB|p3Tx++Vv%}}r#F=| zHI%BjTl^$6^t5%-He$a86rQeiHFCBLV+pY@tB_4$YfQQ$aE6muOK!5~UiAa^5T3X6 z2Kxmoos|~XTm7UP)NN0#+3T6sul8gt>w}d$GLqP$=GQCbkV#*x;a;5Gsp}p%7Gdmh zZLsp*Dp%hEXW^xKe0YI*a<{ZtRe+%RRr>92nKCWs`s)=PTXz$NRXlLTB`^MWR%xk<=VDHQ_ed!6(fB0!!!9^6l)dkKb>~h zkREnJsgEtzFFK;tvXD8Gjn;K>=8cVreEO0(-o@D@w^7dg?a9)LY>Mr^u|^x-6|DJM zF=w`#F`hZpusq7C^m}E*H0k8!0kdvV#Dj0HVe0%|ulPGBWdL}O3!4}(zg|QdGLo+~ zyC`U!w{u6%u-*XcrDbB=+dYSj`NB66KON8woVhg$ZUdEe_sHvrxE@bICA6{3?nuNLZ9EdT#eH|mQV5zmi_00*OpUE7nGIZ|M zn=OE}x)t~~_jH%J#V>kz&+8Q(_^^4)&SzJ=gv!{mPa^(wK2U})$`cdur`UFGRy{9A z3FHo!UN92en6=BYU^li%alv;U(_RB@T~sOci)-Z-!@FQhNJ*0lYGj`#kCrj(X4L8ttobok{!}^(-Q*uv#Be%YR%O4Y8+VEH+|TMa zzP9C8wN;$@MB_HS(&_V;c8uuHF}dORo)RD^JVcKH6C~xB#$$Sh{iO4D4bU znGN{2AYl*2ylm4FnawQl(^{`s-e#90SLWbjW7>LnQE9@8MAGzje2MKb=N5M3+CN&a z8kf*8-acH+O#6zl z*uB`vpS*lutt_f%_=dl#>q1Xen@H5KHrK0$)Q3L;PsN9JU9}>S|6^-K3aE|fv+W6W z{t|Zt0%T_&dxB(HV64x}k)`}}0d*ySRJ@=dHjg7r4G;=|tDiywY_>CFAc5Cq;Nvo! zp$CYrK=F2&pg#svTT+OC&89v|0Pg@zwIx6qbdHGy*p9?M z|Ee+;WP-%H&+YqeU~waL`Y z0|Xg}d5{?)WC6p?9fl-*cMnkI^}sfEXgVt46!wFc>n2<%(S}6@7?=Yol+wHU;C-^% z^%~>y>pmGSNJ~K%)!bwG95wx3@;l|)gXl!-&I8v>-+5HYNb^UTr6Q4$TiZKS9jlkG3}VHa&#IqSpl;)&KX~d;I_g*lHn$yA zu~WtfYY$dtTJkC@NPOcJFq+cTR5nV8%)Zv^`CiG|$ZyFm!NRuQAXmBVT}@_jNPLY; z;>6Ebae?bw)e6r#QZunu+8K=uAuZl6d?ufIZuLmqBAzjHP-yQb8Rls1ba*b$pC3kD zzC{xiRhX9EI>!HcME|&uOJt1jYrbM#iJr#}!kJ|4k`bBB@g-_5lA$UlE$fZZ!x9fN zroT_Cd2?MDi`g;`t2!UjZ?~o9cU|;Oi>-&PaD?heY`{sj+nKkgm6ukQnAC*bs6WM8 zbEpbW)C}Vlen=8-FxyQjM zej(GrgV$QXg2$T|;nb?8qpWCB6j1!DKKK5HA)|xPrFdkmKfin-8M|%n{h({&4AqI# zxeE{6%C8J_+H|b?D{J_42dVTMShdVEQ2y*t=5CR2mRCkwUYA!%F45FwzOHa>zTPeG zwNYelbp}6eF&4p6Sk8eDqy2A(g?8 zt*&BdYkS;jb1xOCjBRIPlLQ+d&zwIjfeEO`0Hr*ch) z#09Vk2Q9JYtSIt)y;NfAJve(ljlU2z$)97-nR%{9y(J6lF|F;q!<3ef&1_-Jy|kHo z-7uS{joz2r&{L2(wJ+W$bb!Syo7UQe#DxFL>8JPS{aP+-Q1U71VLXT~Syp)%Ms@Qv z4g@*P_HC%x(6P@ld@oSzb9z59Rrl>2pPp8hs?UH;cjUAa>Y=bn?JwI=b>|?bnW@-S zq3eBF`6K5x!)Th`58VNq!glF5saeKZ&(AK;+;nfxANnjhb5%D>(l3|YK~FVz>a~f9 zV5a_%C`P@5mvi7lfJtodbZkXPrrRl>_Jv7x2io3xvK*9XDT>-5&!;`lEH3b=1%sBo zBK3|AHu_Few~xBrgQquh84k2s7{#4EdxT#Hmk6zBo%=4%~)mSQN9*Yj8E&86pYR?uY5;> zOqh0ARo#amnZQo!L1lLS74qp}dCp3YAp`cjH|mU^emYz)Fz3sCf_78Ub1#pvmJ+D8 zD!-DAdf3~SHLoskX{?UVS+Az=pku%0+hGRV`1s7Kj}gM&wB=bt z&8FE=_Nf|{n#bdnr>i|nY6J8d`OH@Nr&Q=;ug=7l?RdUdI2fH&soB8t8K4tG`9#P2ZuXd ztgH%WHO5SOdp{MftEK+zQ@-~6m4JZzo{RlS!6&VnnraPe=&K=@t8yjExWySd63s1_ zITIdc%~~SQhuZ`N#ER*5jNGd7%xqJ`#05OoER8BseW$H86YR#@_HJmPRJC11t+Fys zHTU?}W5cgjrjmrkv3IYKrFoiNmwVZao|_A5C5^9Yh-*!*QM+NzQ1ZnwZ&qg}&%0yQ zuyV{;{1LOD#H9s^OM9M+^Jyl~AR{v<&_*4ES}UBcL`!0v}y zPLG?wua-77!Sokjkulp|;$&mpGCdN$$Ztbgp?)piZka}Hv=|Q4&f{O*F|+~wRt5K8 zEQhQOe-||1Wneg?H>w!aZxS7*V9PsLcVSB3C8y8u^MPn&On5-GM$XrK=( z9L)mbw|~7%X~9JkF)vImTv8KQiF(i=498xJ;EE?&MI z>oUYkm2Pq_Zd>7H`i+f`H)hjHJuOc;Y3=M?(07WuJK3q3d%a0lO&Jp%kx(Vz@PJ3O zYTC`pCLy+Zs#k}D=UdBQZroAZvpn|vIE$MVl=6zUw3Tg|Hkt z#kNs(G4fa1(_8b-(D%{oyegk-JIJbR*oc<3%$O$6Kzp>k)X|rmsc{M34mVre8DFX7 z)_kv*lQz4Om5bQlTc#5idgNz5zR@Bjnfy6%*!*f%yY07~INIkuzR26fwcd%(itb0V zMISHA2wlss&PP2ms2!$Nif?znL1oZ7L!+K4>7=fV_T+w;mJyYa`6$0kCwIN2O3Hk& zA@jC&57V;E&jkDOk^-wTdrz$wGR94}aQ)LN5mn=>ETcA~7n7CVvE5GherE7BCfMox zjjx>FlZUiMzQ>-Tzt!>1?qx*!$*eHD;$a3+V=pl}pfR z{fi{JCV_)Klaa?Jzo=b7XC~NrQ0m(aKKoo=38e_-defWgYPaGK4szV0Yxs|c=)3Ss z6W{;v6r&q=;U!n#Y~0*XYcoX$_p5^5&u4u;&ScbOR6U$!D5|qIRmJ=A%r6aT%%~*b zz%j59weu!DTJEpG2C2qzQk(tc7200&vt2HG|}2`Wgy5J^Wv~BqU?!p zp@L2Y`g#U7D1KXIw1=rAgf?qHY+{mEZRpgsR6UH7&*p}mAIBJWbnpjji5gw+SJDVu zgZ7?wZ&5SLj0(vF8ro;q-cCGk+-)nnXUHL#U!!H#rrZP#?HEBxa_1UP7#|jvQhv+WT#)VkAY1s(p(x>y}R41*w43&s6Za6G= zx$hqUja?+-VQlt(Hs0K5>g#XlMD{M%o`?~O$ya~oYHeT+&wt7jpC6&@>hTs1tLXgk zbAIRU@=DUyGVl)sA`j03RH|kZBGh~W^)3bb5eu6h1Ilj;YDlvQ76j`xDL->CkN;yr z51`gO$qBLCiG*b+PI&fpCryy+ddCMV46m`%wNXWfs9tIo|L}RpviX?E-LtnzG;H*R zC~8tm<-|?oy5D68{P9$||AB&rWHN8d-79jFPAZ+FQ00C@@t*Z_NL0uy#Xx1YF zCp+m5fSg3af0>~QB znF)U!!A)02SdsOA!c83}v&~`C>NL0u0lx`sCqfH(v=b~dVsiUY71x7cEX>%<>U$BGw~eM0Ub`2P~Fcdll~U^RUqc@=WH(Dlnt|BsCn_(=ecw z|20+#iDq@&1_V+rU?Re{ACa*;<-B;zI~rI+h0ldxEPH)$s9ga6B7*&D#}tQF0)uVo47?INlFimCdrfM0AbI>DgmpzoDDv zHc)hhMFl7avi~6?i2@g)!3aX(Y@qd3@;Pc`#*YOfJM?`tee<*J)#t8SSc~cx{4;!0 zOKwkQ!ZRNGSA>IC+@xfx=%@=0v62patCby)Pw}DX_^E$wacpClM zRr!?yx^k6>-UeQDYHbRPbr<#3F2bs*(H;cpnQQo=Y~9Qqu=O# z@_t$I$@7NTv=+(c?}Og2xFXoEJ$-O0{26EB4?c0oYvaj#vwepQ3D)&S#Jm`!~O99Bg7@RXz==_!u~~KhHoc_o36zL!`m}tZS3GsGm&97*QyAVhTc~E|~8g zm%yu75s`-6H=+EvU;@HZf(%N`$m?O*=k~+pD8d@498jBTN~@H6k838Uko%TR1<-Bj zd-TZtOPRnYc!0)>$&nsjqI!gv5fe<_qy&4qNgW8)aE~4}iO~XMQjTy5Rm&t<1^51? zG+A)GLXg4d5Onn&^m*vbZi67ATTBrB=P!xcpM3fHam-o-TTr%Xd5DfPmU@|fE=FWS zsxhbT%2h>mXzC9+mOMEiW=W*AvQJKPHnH3{P4~I%xs~uhIurjJQ@o$;1Rsk;5b2wA zzZi-Y=UZ(%scvR;gF3cN?dF5&ZX<9OcQgI?POGN5)Ux|SR0A^W58;ivs%pX{)gPd_{%j@2|dEIhvi8Xh}1~-m8qd$w1+8TQ@_pTiVjf4y(WcuzD@kS>M982hSL#iEi5PR-_rBZ(W!Ry>HHFt!bd} z-ZMhk)`dIrZroqRq)J{#a;teNBWXL!jqZ?0)@#cLq9Q*0U+*d@O79SCv zVAmUAohqP{d|OIo-*{9*uYN%*%G0PiBFd6}0>g>%G0d_le1lcNyB6?~Wd=-~)E(FH zq&LV8lDK#`qVcCnjwv&-!_)%S8djXNMF3oqxV@-|IP zGKy~Z4Aj3+0tC+E(1Ry|c==e=)*=EQWCQJan0M8BIC*56w6WFxT9=8;r*~?e;5@-W zRa^FYva*Wm4U1x^;vL?5(}BhQvO&g<)Iaa9-T~uAmH}~CoI;jTz9xtXt`k{BA6A+G zQKJ%Y{!_>Z3b(IS?GW7TKyQq@0Ouzkq-j6lfa=-oK+HFQxhX~m@{RvG*fjJnAABKw z75MbP>|HBJdkrH``lf_Ch8{{G#l?U9G`Z~c2oHhVuebV>*iV3UP3XUTz<8PE(rZTi zK$g#Y2Q|4FDRX@u`>*NIHqp_-m`?tlPum+GiL3%E^!Z_%Aee1afq&rwj=c{ zUUsu189X^i3D@L<0#?SplGDToC(2BaVf&@{pE|$Y3#hR{zq(*!AE8N_4x(mh$jCI53Zo2$eW=2B}Pk z?ers|W3#^sBd`29ITO9&eraFcxqU+@vxKMQrj4GV<`kDM<4vgGjVh_-kCd8aGb`c- z+0@J3Z{B>dxp#a=MC?;k;5wAjKqqTbzbBsfi3bva9vSVrpIq)|R>fa>&O2)9UG}Q^ zjWG0{Q}b&IRh16gsUP>6wIE4_kxs&TMBo`KJAY0 z1&&a#sBa7b-)ray8WaZmU=M+a$7(!w1MD@x!8~kj)I1Qh0HP()9u^^R>BmQwfg>NR ztFQ+al>#J5j+S(k1Sr)!UZxse`@jMzMJVmeQfTrND=2l+y&FR~!Ch1aset_!T+Fp4 zmPACS4P+kwj|ZB^9B!wa>S@KM;4F#u=dV<6T>9GwkM+XCKyUcsklmea2b(k(xg&Z|!LD-h#9^nqe ztz#SQ`$_YQcpOB|?{ZMFxBwCYtD)DHj$&vK4{*BP>V*cSa>rrOeP|>Y(Gzpf)pDhk zvDB5MvE?qv(k^TSZLYJ4SO(IBtyra%VW0UCL_K;Lmg3WE+OIgoz*RVIZA`l3i0MP@ zRzv1uAMv1eqVT;)B7bq_i(X*ka5;Fkl7uhhA;}8RJB0!z`yOQPU_niksT_z5?`|+~ zKc~8K7aSFE@2~X9HefH58udfN;n868iRxYE!Xr*NVKN8T(Y! zC)}oAC~{toxs0F<&G5=Dqwa%S#ZAs%rTS-6EN`r1TO%9)c>BI^JLKZ!*2??mo^ke8 z@LcbRt4-^V^SN5fCtx(1kQI}sr=?=j6%Ce^3oWLKYTl97mvAlX?V<^36Pa}1!)Gh% z$$K+GdFR02U_8#`L(?o)p zuCjA(y=P8`@_J>(gSsv_uABnd$?tDN#zY?m{?|1UCcbEaz%MVG0M}fFOjd; zDAJ?vfM-aGYe~ySU0L?|K*QB}ue!`7XQX#bZOr<@cnWoC#AineiQ5(dSJJN2iy7S2 zB@(|r`h98AO(r5BRg1jC<*U~F)xPqmG9V;7pT@Mx)3{vzULKoq@4W7$WpRa!YFSOx zw18Vhnl@}zUX_*ysYh+-{jKERkb)Sb|7$Z}v<5MPKJ@xLf|!i}Q_T5A{<~n7@*>PJ zRPy}C5jCiK9OhsdKLXt@t^nh|KKD2DtqbC^OD0SotY9i3{4GBs0|1dJWJ7uz`a6z0 ztRdO&;VFyJm}AFrtQvmfL<^c$#){s1v2M>n*2Z{GwC0EmY7EFJSz6G92{bj2V8GlC ze)0~UagJs=DEgl>4iUB9CmeQj3w~vf!=L{-`zg`ppGS_M$Cxo;;RtNX$zuhP2NlaB z@PsAZKLRd0GrtOIuhpAr;mGsj%MP+PJG&(A@20oFU+c1`L-ZQQfvTo0rC>9ksR{O~ zLJnimr18UOzizRYrAWaO!o}S2G#ODX9(Jav();GkyK!*Adp@JDrvx}R84996IAZYIK_7K3ZA_L*#)GOz zjDAUtW{BmbblVnr6+Zh*soX8HhE`@M`gy0Xn0LP+4<--lkL%OdRShY$r;c?@y`dX? zy;>H;f1FbVkPKN=ad&Vk01!;Q`V`>izH_dBQv>eo11ob;IMzSgW z&S>@)CSB`c`hKhf{gA?-tgiLswN^IA5|JD9ORs{y9?yOI&P)_4eaXn9oXJ_3EKrcQ zgNb4l+>Wx~<+gRs<{vb%&gMGSHxxNy$mi}RxUHVJta&YgxwpqUldCAiwWj4>#dGC5 zJD23%l$G_U)N3n7cy>$kb5-8FY#Fk6?j*Z%V#v9Hl9(Dp&XoicdqHpSmder1*$ZY1 zPTTgpWejIBEYj;%+iayqyWGo)e)T>DJHcW6xt7ADkD|ZF;yVH2?z1ZpMUdtFO ztzr37O)ni-ZoMhZ5X5Y|S>lGMJsjRK2aU_=jY{5WR*!O^KG93Dqisb0c>Jqy>9at7 z>9egL5wWFYUxN-#sH=(yLqj*GiK#DXC|iVGe(8-h?nV^>s-(?-%fu8Hs?Yh%I|gzzJ4c-_q*th>;9IpxY2 zm_ERqu_^`@bG#EctLF;N>gBV`Pgm_RRDG#HJy1_V-HuxNgkCe4PL8TZQ4mhpLA`*h>1k{Qwrz=0**gQc`c1HJgZFBG z?ivdC?}Gj~njXn#$`O4>ZN}dE12l6IVQM5kf*Kr2hgLuKIs&Kwp{4fEKf6dOy#GNj zfF%&$|6o21FfB`7$v{pB#)<*moLdD4Ge2=hntxSuAa&h1{PQYEoB$XxICu3g=FE}K z7=W`7P!B!+3?ATC&VD3zR@bCix5y9MA=m!p^(4x%NU#+LJ6~hQpIb2O0lobZq;IqOX#}LKa8)>6XosiCArbMRglFQSo7bf~3{hW|Z zS}!&xI7w=Ij7sdnhc`-@SIuraTr2n*8xylszNR4T8ndbvFx?lU7?S+)bV=4&+@+1@ zG_mZ?^XGN>T1;Jp9%&_9j;WZ@rA@;Ib$?rU*G=bfu3}C7te#rN?D=Ao_M8k%<|@y| zkVr`3$j@Zkwet&W5oAkL4vy4~#l-w)b%n!q85XTEkqhIi?>=vQ)*Bd@B$HMD5gT%wrG}(t3 zFSMC3e7l}CcJ)hZgiZf56|u#Zw%EknN^R?=v8R?~LpCA_6ANa^Lf2y4bheC!44z<| z>@y!Nr}R`u>T)J{&Nqt8l_BUy+sFBFHs>rcjs^CG@8}J(E^t0G$Otc(E~|~W^+R7V zre9`|uRDOOV4QYABY4+FH=U8&cD=RKx?R>#Wk7$rPc^(zFC;NsZ9}3_bzB=8*U>vX zfw2)U>xqsGii^^YFzLKHske4~ub2IU^^~!UA)8~dz{{FY%)zxG-|~iCf%K~CCqWol@GrTl%s4U@UdU)}~NQ_JgTSIdjt!A^*JAm>^ ziv))t_^Wr*RvE8X(D&F^)_luVsA;rO&cVi9>d@>H?ZcEf)mreIT?>iMd{Q|#F`1t@ zEf|xL{-42O$KB|`U#Q7`N*rwzQDMQSpK#7Oc3PVCPq9^a7L$YQXg-6zrUrB&^7Aq* z{dUB(f~6h7G`5pM1jD6(7L15DOZpy25<7wu!s?C!V3vd~gg*DDGKL;oL3*F&ynjQI zP|&5ljGwqQ;O-&7WIm^mc*s!7gFA<2jm`D^LiHim3-2I**`NZ`K_?smO z;ct@mWuTjQ!IQ@9(>A%U%)VlbLoBg!W}KyBm3Zhgs+o)BSdQC)_be#}tHgK|1-yS{ z%qwIS6i8?}JC!&NN5uj9Z%JKre57-HT+h`+-WFTbh@`W!XkQVZW?gUH0GGYCX2qAi zNMxW|c`eoZ`S5vhhkoq${<(PaPtsfT8*h5-Jr3^J);hdu4K$_6&wiQvD#~{#`l4>b zfl5L)Pob@kP-1T0r5=;wz#31CR$9t9zj}Pm&!sr^^kDlY_la=^R!^mgnUKONJG8$> z3kTQdPzj-nm1G}@UTVfE7rb5F>z^=u=;54Be8=v7O@~I#Wb0V%#DY}KLRN2X=3#wf z#yuX_QVDA1ITdkr-nnryE&;7FgoT7aSt6@5mX}LPWjM=pwP6b#0f0`g*lWL#-7gy? zZ>^vj&fnRdaWAWj5>=&X280P!5@n{s>F{*kUf1z+hT@Eh%4Mzvj5k#HpSUzCx5X&) zep9CkRu(X>O-RJewF+cg4{1iI#y?E`J}i;W%dY?oDU+dkreEi+7{bl-hSHTz4UGUZLcIOrrHCM9mS-jpQ-LG&Pk$P}^)>pThshMz9%t4* zc^33F$p0g6%tg62ad+v+7O~|n`<|@P6)HjyO@@oJ2K{~b;bdxI;1!tQrU9dqQjY2U zeC(xhOz(eVB_%*O!Ez6XZLqjuU6>}&3g(Z&wE;>eih}@a0~k-RZj%-GUk6mtyRN`; z0?X?WqYbzofzlCJo?%MTixeP1XGVZ@ zFZn}U>tjElMt2d2LEm)+_qO;*P;dRw3Eno^zqm25#^cItDoB8Y>2lzB8o=6tlP6?_ z7p9q5|5Te62b&2P83RO>hrx}4hR=XMgY-Ze>`tczt_fB}XN7T1SDL1O0A+{oS5Tk8 z<|Yi){2O`(6d=B|sH2^{f$AxB9uVyTM2))$5%{|N6MpqCfL4~umktNClN75V53%VN zk8vPJeHlnVz(lJr9EX%g%Gb<(hrVGuK-c&oh;A(cQ0l$F0l>Z@dE5k8S)|-QuaU@q z1lkbfKlbCGc_8;J<9C=Cwgb@PdQ3PV9Yo~MeMp&-0$pi2lw$QF$UP6NAS@P{As5L5 zO5j<-*RM#i?vsvmk?MWFzQ4~3KMhU^23MJUST`>kc4z`p6yKgx!bq!g7HGh?0(wJBuS$$-ln&*s+v#U%~uM&bi?$Un6OPaw#puo*1cMttC{P`)ugG71_ycTo2y-X3yY6;xJG*MBSY=v>2j zMfz#up|+Tzk-;Wq?hJ6ij>=$HN(EzT`JCHYyIt0 zdbh5{j}!djbetF?73#q9*pO*PmF=?OOtMjPy0qw}d@Tp*@&oFS^nvJaY6=Q6X>}Q6 zd^?W3D#Ps`Bc2G|ju2*uXr{klMU|d3Az=~vz1jXSH$B?&)#Szl7CVRH>KgtU-fz|t zl3{U3Gf$oB?u`yUYA<7DdFRSQ0aIgrH_DMl(TP~j`xBAMfS9%DT{_9AP*d5;r)i^n zmC=VZ?RzkdapK1YHWTFIf>IShy0*)yapR9zoJWMkRe7!H@2-4*v@B8NhJIK#=H40+ z5fJn`iq=BnMwg~KWq3CEEj>)Eo7d|xmI%Hn9G zgTAu|$I3|FsD6!o`AU^hO-*rF4PEK|C<{y4Xj}9NYw;utWtxnLVe*p8`V439l`OjF^ zq>s$R>Tay@t!lrtW#)qVpC4~Lrcf;n-}aZYqpl)h_rDhV%^5;W80T(+H|q)rCn-P* zq(K>{02ewJ2h8Lc@R=`qe!`#AoUU2qH$**x1ft$AfN(Mo#KQlEhVTjg@Cz`T&1oS@ zx)xwIfom!bKfV~lC-D$pThmFhNnRQO&V2Au9|FFUEPo^io~t?;wP!&I^jtI`AAN4t zHG$rJ5Cu8sONo=#fJXTv@qbh&YXV`Cd%)RW1jJw_ziFLIKs{Wr-~p#wS;1*QBrOr| zn#o&WmpK5xn;!^)O$7nz*{P<(KsUs}2I1DkLP1jyB`6+L5hy9sLg4=-c=@BUi*P;~ z$DE$Lm+K_dITpmF!GUd1{IS4q&JV!R_zG=sLgT1WSbs&gB=4PA@I}mq$btmGH93}x zKT#oZ$uA29U71xHY^wyme_+1RBYlfFB0%-+4((Isj1nad!6yd$`hwiMVu;T|EpeFL z$py5?-21hT6yR6P(H-$<#irN;XcOc|2(ZLr2WOgBjMo7k2O z0Lu`B+IkIE3%^J3l?D_zU$7jJMZZWG@tbfb2rxch{Q{oK;DMh^SQ!j`*ad44mC2PM zN!;!M>nczY|1J!2Jw&1WFQi3)-F9Le$PEJ=oB<>Wvm7Z3Ytm&wgenel-vTR?Kq8=+?h@DnCPY93^6?RB;V}WVK+NT~5uf}MIm-p9);=l%?6{Cn zU$Dlo^;I^_JsLS? zv6@0m=Nec6Q$bX$5P%EhpvNa?Vc}<{2O9+8XSa>mC9En6p|;MW?%NZh`^&*;CP66O+A!4*N&JJPE7#S4txzHsUwiL!fz-& zJ>^X6e~em?ctsI*7S()p6<_xVfPdBG7|+YbAB*!X8@_n4-d^%L{ShjIw5T?Qn zK9qZ;jQy!oW)3<-B>fvo0Ao2M$rlJP-|DD$-Z-Qrz$idBSV51N2&4Sm_lSiG%x@RC{4-dk;#*=GWN05Mx-e8EGps*83${J{}pt} zXF5dWWkc1OlVnC~GTvULRED4g5F zg9$$cFh)DB&jJ~VNoHT*&Z3w+_RycX&KGQ(ADMGW;E%(?t@@A~Xwx&e&JFMn#l{${ zm}=@XGrLO=Mqg!I1UuA8j%0JNcXkTz-1`hR&CKqhsL&(bRwtMf6avs5#H%K5DL`Z5 z44`~)9drW*6Kd1A>sSF0e<0aBRNA105F-c`$PJRW#b+W2lI%J_G4?m44HRX~zzPHe z-MN4yD)3xx0LCL;sKK9P=LHCls6dpOTL4nRUU_gN27kZn7$9@df%Jit^T)Nfuf#$5=t07=^BmlZ>QGAD*~58vQ=7IS+fAup98 zpS=9khi0~Iwh8~sMX1~qA}<1#fwC6HGMv!M%QLdVItXRn<``-vPcjTvdaA(q$X%L` z?U6-8fE5X)>hndzUIv4@R|WPzl{r3Eyt(f$ndlL6j;JQjrAldddlc z#QrXt1b(;uHc=o=jvkzWatGB@dMR48TyxUSW6#X}GumOvKHPj|+9k1aK@v#H31b!e zdmkMbXX7N~@uJY`lBpv;U~G?zbhqo+OQGrRvSJY&h;c_~ z045WOVt?o1x0>WV=RiQgI)AO)1*Bm(yukaUz?{Dg8Ii^vqvT-0T2_|4R|V`8CB8Ak zuO6B``a?g*N+&>FT}6fLg8W^|JNU-_jj&~hv5jd^C=u>;L15Uj<6#7RPswZLWUNCY zsQFev^*Yg(VH+OuT7NeR)fC10^|P<<98r6^t<66xq~J1+yFbJ5+*I4unS-O~$4^GR zV^^B*D#~-P1xTHT7D^Y9AB?`()}X>^og5m)?c}N&)kQ)kxq+_gWm-dvACr7-0hBk$ zKC`||Ipf#RI;(8#ZG2q$a=&uFqpbCv?o@ZH@9GU|3vY$@kQLKBw*oCrK$xB_ReK{E zr$=njB*oXRo|634yzsbr!EgNvg;b~&@%|Rf1%X(f-~a)EEFK3ERVt5ILtCt`q0|45 zdYjDQ9s^l7jsIL*@|`t99iVrbTjB7a52q45ZDX4FeW&={sj%yZ?SLgT@(>?AivxSC z&L#lANJrVAW7jNRKTXFQv!I8KWI@v5EEr3lG`w{W(ZMA5_`8?kP&bZO|p)L|xMVa6QNKR%jlsOZQ5#{WvR8zh z!4AqYeIX3*hd4Y=T4vt1`*G}U?DdT>QkVn#n~N0(*||X98uW; zJ8_^lDYQQWibNzt5ecpN(L$`XvM<2lQ?D@Kfh3}98X-Vi(&_!V{Gy3^9d;OR|o$jP^notmIkQ-SBgpz`?+@-F^JrEmlbs z8Gaw6R=u_-`>TwCe^!cN>BA(j2G&9SsO;~t`Mkm$q#yE-go7Y7H}XDa(6id^`#N>M z1Bap%Hm1TYq7RG9{v*CfI>Dbjb@9prd0%TDQoLE|&n47Py@6{%l$BvDy->RPKnq7l zPwdzd18K#M-!OWg4&pxKh>LDvU)3-e2npurVe2##H;JFoT*(kmCuxU_F^61P*0KDO z3bQ7(KEoy=CTe}x4Y@8`nE6e4(|phnmSd(Wf59=O{u^olRXtJu)B4D(@wKcNw^$x9E5>ih?PqFyc&Sp53;p#+M&t8Qvo6r(Q@kNJ(}ztPzjRyHp(3u z{2mx1XR*e&?%&Wn$%Af?0U2|*W4iw82GHx^d45Ci13~3o8|&QWD`6mAn?)eQhyT}B z*ek9pa*G1d^@-k0jn;SCow)k5XQc~G-W^+{=FIt-&hAgEL0XFYe`9#0e){t{j%gU* z(wU}NMgIz01t=-4+U|&@#x*HpX{{HR*xrUep=5fw(7Z6e9-!~@h$1Azndo@I5b2ZE z<;h}%)jHedU&SVtsuy2|$j->ADz2rUpvapcs`a-cH256isW%v`_Vx^M>Gv&w4M?_y zbo7GWU~h)6bEC!yZ`TcBLYu%TgvK{s%a{RZ2`3%g2yB6diEv)v=(mFLP6K#FKmffVHb0BE^DW*HO+d{Ce6aXJL^fE;k7v$ah4pAM9fqFj_a z@j2-qcqT;1{}R1Hnt4RFLi_>aHvbEr2{FS6j!n#~7s)Z3fm;K{0Dw>d@d#Qd6wv{r z2K;#@cfmdpMk&I8E1+rp1UNiC0I(5z%BRUPNCEJzCU>X66(Gm}ns(Hm-_&}0=&8M< z8#R)KDk}VDZX?;A0o5B%(j4hfE0RkSss(JrBdRmz#t*Lrk=@gFI?I_~In*+%n11Gc z&ZAFmCm!;Y5*220hM(H}(RA#rqE9gi?-$5QwE3 zWaeQ%9pN;TK++(H_|I_)s9eGrf-i(^gqyYTUl>;+M_0(i zA3(4V=6p;Zxy}BQYIQ3|dS>#-9k9eW58MHP9AU6KK#mNc&Ojuaj5rkWr#k?~xB!3% z0H)ReXc#d-d`SV22H*vl*Lx?g0Cz+irpR1djzo~uZv@7;RFW3WSP^0U(C@(e9J|9i zup;CF909s!Quv4d$IXif8tQoi;5V>)zQB-a%NF^Hq zUMk8N=QrsA7&4dmKQ96so|sU^%i1&Gd5H8waR=~KTzeUdJ54UQuK+-{?ggIfG+Nv* z5Z0J?YzL|7z}L&GKND6oCeyhCMyTcd0AR}y#gd>M01D=W3CRGc^)lN0d)G1gwu7j} zKUL<7!1xf6BQ6@qT>-8$-9_*t0*$5UP^V%9HN}{@gV@-Bag`M_s~7-x(GMR$yMWcy zglb>|)R(=|Jj(50Z3h8tifB+4=mf*ZlxBb{a=O+78yo)Z0EMmbkoN?56j#aKX@DYymbWggi>#iyFI$k_v>5{0{olMmX zzE9LC0ucAM-43hF$oFrZQg>Y_Y{zVtzCLy58hOQ%2$7&aM&i9qfhDK&8v?0#tUrKL zRi+S_gM28E^?G(L=To5AAZdVX9|Ar_K#mIq%;;4hGX%m0AiW>ogxp*801j#3?FYGY zC<`_a_836z(h;sE-M&h>XaCQy0~O>G)Z9ft2`b4+!ZRkUm;Ny(w$({^Bp}OKA^MAhpl!)rIMIW95>Bhk+BnOO$*|IVGaU`6W2<8_H0J#Q7 z);{F|$=_@?OW{PgkmQY_e>5D?nV{F7@9C<$foEf&&s?+;l zy5--R^4}Z!*m*vR3EPiJ$bFr|wXoHFA~5Lwt?m9^Hp=4P+U;gqYU9J2$#8r$g300l z|MY)q+5a}=84j2cvvq$B8WP2zAc)4)|2oO?(GdT?Yu0~lbYj%~6p4+(^(UP}*~8j+2+=l_i@Ji8gu(pr#1@kXh zDY6L#F+Pz_N*WuNBd-WO`ar>qlsmg(7i7aMt|uYKBEhi$xNNvAxMIZXo``H<%78^9 z5{nrFo`0}vtou#w69CK6Q_kLSy(uc2xQi-)`9>7W+^IPa^OOtqcZ6CFUeC{g((l*0 zA+muj&+sp05^6*DWWjqMHUfEKXCfnanzDCjz%GouWeR@q6Qf>9fpC$hYqw-5VfL{C z-(mN<-`*yE5}2C#brLeZXy8)-k5&Pbtiejsf`k1v6~pk)&l(G@Xh-faV7h=U^#0Ep zRXs;{}cQX}27#dl5L+ zx@NUW^`PY;GbHyWY=<46TKDz>Ml&h*#JTjC1==|MikC)aH7bQ1R-e2T1dGDnEO}gL zWV}h8pHFhqSIBoVt!^}L1Y_ zCMoAnc|M?e?e*!FnYq-^+@xk0q!2>vh6p;5WR90TQx1Cn!F9w+xk<%(U>Qm*NRHx3rC{I zIFQ!YdhL?Bq{2>ey1U1PMcn)db)GmW|T5*H^jQ&!MMPvo~Dbw!^5;;uB6!}B zUE*Ebwy9!|_mj-0CP*5!@+S`T5{6H0ncSC7O?C`jvm2_7H2J9g)RMvW%aBW>fLcwZ zqKa%^ul2VZ>R0rglZ*z{4@Z}VqO^9V(lT4;8(SpS>yeM1WQ%zs+>gIHG|lDsELl+f zqxEKKfW#$sMhDK&k&N~1je>qF4QUTn%gSe&_rs?6=D>osmbS86*WExy?6TU+9&bk9>J&YxRX><$eJ46$VQsYW}B za!r&u1H{ZPjqV$i(>UcA?`alFycL}oJiK+(sDAl$sa!!J*>l_-WF}BR&RT#;U&Z6!L!;lVS0aE_pEOy&rEf7X^6CQ zbMxKTGlr%S<63gM6c0szoMhLHXRXfh2(c8nptI0BSTwG`ZBp)a&}LlYj4s~3C_2a?Z3D%88$LeXewuuJxNW-&0QM z=BfLO$_=_kMt-<2X=W{IW%ec!k4xOMSphi%2mm#PIG)n48%l5Q8_JkGV>=hUvQ)%t zCLW;~@qp?{iJ@>t=e9uBn0k$+K^AFS-P?v=d5q1Y@v8q9apxV7)ZhMn8&{&E~RT%YTMc+Pdb->-|p!{r`))AN(kSD<2v2-Iy#&uWgrwycKIAt?S~ z+A^;CNu6q7<)UMWZedUIk%Akzl5R?6G!9A2unD~G*mf+TF2r0t*GQTS(RX#uX{LvKo&C`zM@!$H5KulZ^RacFw<7k@hM3 zd7~7%-^PG#&i>G)oV=b^G)Fl-X+RETXlFw(V{w|Syk>BXIW09k5M~fo;}3J=!u0E`&m)?$zBLcj2=Y$f&hXS#`O>HBmhP9vp9{W? z!JoTlxnie%=3-*g&h&4H-xn#!@6sj$l7-lx?@8IZ^8v%4ISL-xVUCk(x!vgt94o~l(bk72t+TeA&evgrpNp!wd0YV3r2c4)n+44u z`NPON6RjXt=J#U5=UF)rK2kt6Fo3D(X&m8U{$|6>~jVNr6(~zp`=;{df$6$5w z3^??7k*Ea-BAr$hwaY~TF0W;NBMu=f;^OKgny_Mb#LL6C3ySuD8vl&p8no#w3w-BM zQk`WgS4JWJB4IE+>va3%43Ehn(u7Yr7~JgBSJkqw@cQjV*sDH^k-m|OGlD=D&W&z) z7>zWZ*22p-H?V&nBv*$USqbc;kjw)621z1%wXmKH&^$`9kLWYN4s<}Orr8F(d&&$d zaIw@}xl+p`M$R_m{}@$&t5S;f%jsz;Wrq0&u-vvTesVv}=};{*53jpYe-$HR0 z$5Hk*g$Bt@g-+(=jDcPq%ouhco?6HxRj_Nu>PNDDJ9WWj(rY9Aq>IBb7vI_==#S=0 ztYS2LR{Ex(#tdNma!h;fopMidSt*yX?}h89ok3M<-EC?^s>RXtp|JZ^w zdg^k2W?><_>8nm|;s{a2DRFoh(v_w>%pg~A9PCr8Z3c3b*R!ZpMDxjVLwyud;=DuM zYdloz5jbpm?ZU$7w?j=!p;6T)`H(}k__s&fY@=gt&{cdDksiCEkJ5oeY=!`g^x5UxdJ?)S<kaat>xZjJ57Tt(HG9Bgt35}NqN9_%LTSp^S&nM{D zYfDbl^_$wk_(|TPm4v+sp&*~GDyZtdmKqZr^$4=9fr=k`>apmUNGxIy{qpyYbBNE^ z+lmmX__$ewcCY2>#3W~_Bq##O{L_4=u&rpOGJH859zX4;n=7sfzW>sSBVR+=xjz&v zhSNMd&Oh5_w{$*;-?FQtj=^%eF^hLKV;`CfhWnUeOd*|9kH?-hG%zo=^g0KF{|GUNMjfX zdgJ$I{D`frX6`VGi^}`IK|)O)c-vJa0gikRN0y9lQY0_0CW^o zD3#~Ckf-+K)7a}9e?bKz1v7jnTRSKV0J*tR)d2rzZ}X3731 zqAf0dZ&nCJ|7?;lN6&O2erzW&3ks;dGH)#c9pTN496DRYH}F}VU)%YQul?Wev%kw0 z&QjWZFPq*0@*^Y`l=t+^fzO*V**-w@cu>8Loxjn4$T~o+@=_Tn=5cA47i`k&%gVMr zRD6gKDDkOVX>%!K2!n7hQNT@d8ypar!T!MSPW*g&6CNJRtnbKN4Qaq>d@3czA#n zt{WP9YGnt{ThuA|Fk1(fPzD>Fd-WH|kB;E0TxJk^B|4;g8y6*VEkkY}Wc2h^*n0@= ztD&+c14pVzbQM*6R!!adSG}Q>rCxP8*B+GTbpf5|x_m`5!gO?|5yNh-NQo#shYkx1 z!oZcexs%-T`AkVKOGCHbud|2Hs>O7zw|gg@U>}R3IB815->9*Jjahx8zb5-U)Lda< zkXX#%Hwq7o^RFOFr98sb}joy8@WqzWprayP%S2-xRys)3ba`l9?opxDr6WO~@ zp;1)#$yRPI2zPg{H^WbuAcY{E_+cWfuQ)k#qr{URz@5KA3tZwLMwk|ss+pd%Xy0Df z!L^K$3oc#xbm*1nX%biC^cw8-)(aiL;Nfu6iVDZF zVx_@rgMq&Cg4R5hj&SzU-*N`<@L6A8Rhob7rb=$R^5PnNy8*K>7XRiYyUUJr`oQZ2==zMN#`4r z3WlqZqnFMycW2x-y7A_-L+y;De99fm=jkTogRj2cI`(SM#FXuo=>3SH(UJ&Bvnzh{ zN>#V9NRT)$5JAn%vMqnsUx2BD74(fdWbtfj&HaP(X9(6|!j0NCd!8Kkt6@VP5S0nW zP=(73Vt3HD*kv+hDQ|Qt5a4z2>(SS0UrS-PE_r$?g-d&7Hs?J_-Z+hTXi5iWl zdTs;QDW)!bDI?|^JG~3-^{1Dr>Hn>Et{ux_;+uZRXUt zK})qu-n!ycj3mXu>g`KFin1Nj_%zP>=^)%s!6`vFvrk2l=)q3uLpX%u-1O(_=D6gX z*$EIZxH1y$;>jF#(T(hN?Q&DckLthA@c7}M`|=^hK6VQGapauz0Z?ZKGF^q_kN8wN z6)G+^##z%iy5;7y{z~zFjd!JGP}9X1_!V6&Sy{eb?O3l*Uz02(h(L; zJ^hLLxYX?)9U{Bib4)#V$Sl=G;zrBsMs49FE&+$YL1&Mb>-Q~xtEpLX^;jrt;#Lgb zLHU1Sx_;~0SDroOkk9Coy09p-r*S`1{=yziM0n6EHSG|m+kVHutX|jbB^LByf05>D zOhy(BCC*{sp&wf-o_U-kmWX&JG5H_p86usMLb7A#Y`Rc26m zyH>S9`)6k{L#f@{&}xjW3GMJKLB2o5dBUG#*=IZ3jOP2Tq|ZRa1(nO0uzv~Z4>hs+ z1PGfli2k+k)yu8Wm#r^`*3TX)KDbt37A~|t_8uDhWCh}RH!%`jD+px%LJm{cDhRb~ zjrYbh{qA)PcOAoN76g3_j(dMUcr7ODMY zcr^u-WxosZ8Rl@3BOxIPGs8K}dL8Q5>Pa!^4DWV6C*T7FOwcyPu@w_&j(HH&^AqOMuI< zrp|VWO|=Nnap_+jtbvY_eddjc>s=5R`U2ZTA)HhhXC)a+8FA%!)cS7!5l4~=zI-u) zeQ0fZ?fvZ+MRTA<{WndA;8zs#7yG@?pR>nKH~p#QOvfP&sjKgD8kEgK_1KAY#ZkH& ze6-hoioS}o7UaqF#STGAhTiEZ-_q;Gt=v8BK2n`PL?gGSXg$6pKAC!TH0YwMLC&G4 zmu4H_55dU%Ivw(WKv1Cgb?r|WR{_-w=yXrhSyp5R7RtiXxO6ZH(xzBpjsCJKWd*U9 z%oN)08C2CRf8vxz5|~D=ki1J!7VWBENgdWGW z|3F3omF0gKqd<$b&U=$={I8ar&iN}V?U`5jbEAFJAz=jc@Bwb@KWI5+A&*ZJKzkUFnTYZG z!Z$VLn=%tj=k(c~K%9VTdE3+n3?K*H?(mlr`xo1>Io1O})V|`>0XBd-Kwh&mcC>$s zaP7RpnHz1JHUZ$AM}#!yF-3rIg5EGa1lR<4YJ&K;);Cz2&4CT#%+i*DwPAkWUlwdU zAP?ZCvDWSYO@Y4yJpM7&xZwcx1i+sSeO=o^5nR_~bz0>HR_ z6w>zr`X7Lk9{374>9=JLj9#wUbsTi~Vr^uB_sHQW(NAmi)U!&@l(v~%l$0|$10V=S zhX#j`of*X{9z{&0C}w9lX8db6!FR|gr_O%LW0KhP zU9nvFC@hBnujyCEMR^R}4Hi6@xyhLmjr5w~7jy?O- z$F9$TIV?&6NCH|Z#sxsoDGf4s_BASfNzsnL=Wl`U^&a#Bd%8?# z2#lY`u}()=t6RIU$|-~GF;VL0_I-u;-)G(%ku=}e-xyVqw0PGbB=jaP_|WPQJY+Sb z6#qSHxTEWe{k6PT$=6JC+xh4@=K__eH7$Z`cr9Ov6#toC@Q6`UrxG}J4L`oiw1YN& zA(5wXAS3+vpv#3v!Jq^lu8Wu8vbXrIJ3EXpdVH`f5#p*vR8fW~QW41$zDTRAr{I7E za<&(t&+~1z8=A||RZuA$EU!1Z;Q;$GfodMj-GR&bFTrq9Zx>JEwl|G<(vpxx?UY70L;Wq}< z!uk1YWL|GEyiBFT!|k4meG)o`fv;J)nj0pl7^gI79kTCrb$kvxg!AMVIOuw?Xc6vY zLN?z|^MGRJN93!HeT|v-nByO|d!2Ia;y=PxrAe;dKH?UdE6!Jue6t00*@bav1M1|j z@iRyE7LHol1G~cX8yGsRQ||x4w3=|&Z53Ls++MlfNQ|0W5=2B2gX!bcT+}zO3;2td z)b@HwO<&81iipVQd>Z6*@=NXLxl@m262a++CTsA%I@u|5nRr_cc#@hY+u|{EqV>(#m@1t0TOk$zBn%;3N>lXYJ>bC-oXa&;xuuZMjYs%p8-CJLsvji|}a=Dh< zTUT%rVNcwgN|UC#(+t!N#0n`0%uXCknNU9C;CfC8*|UcoFxZ&bSsI%13ty+}_AU>h zwkNnK+4J;xPy;cU%b+<5LgBO{2&|{bG z^mnI(>aGOkUM436-Ibs9@gdIoY%fucRQI*~xs1(gZGMr(t@LH`e=VXrhga9QzTIiM zm706&`)bKigSoBNBm6x)X`|H7KvjB2`znw%zjV_k3wV5->$T4_`xldnNN? zm$ibEPk(`mQ70|cmfX+cRYBE8%Ff&Uf{itL21VOw50B}etiD0b>s-n*Egu$+{af8xElD7 z18Ir8e10Fav(qh2XF}4$Fd!2e{wtKU3{#KpdI|-j=ru;2Xd7@@{bFSFj)Ji3)l9UM z2Yt9xW>(|25$GYQ;+d`1;H28Ki_a_$D|BQ%Y_)z|x+kqu3GF-29{CyWy{pZVPcBt~ zoVRJ;4YkiM3f1q<3Y>n^cK%;Ndsn9VIm5b=j*a>A5LPJ9p4YtXy17HZrBK(bsPy4+ zT8>Z6zIaVG-g*k{7kd*ku_Y-AvFqeEP*BFZt^>>Q+`^m&d1qUz>(=Rxcj$r6 zh8W<=5xuea)Tm`S$<{d-*)>ZVYHk1&s&VtzJ>M_c?wGdCAJ*ARIGb|1G4#@4)`TPV zszS0o`>o_1gH-tw!d)4ovkxgb{fLnm`s^Cs+0&PX7(E428zkt zbdK8%bax-t-^C^Ue^X`t@Ac?^i_{K3M(1bgm%KMjHZ`~wLV-}&x8Jg{KvDJDI-p8X zN;LvXrN4@Wyh2&57C^ncLuu!up??XLpWWXTec;39HwWDgPZn*P(~Q3c$WlrSrvG`o z!wsX{jZNX_k=*O^-)BvVw<;+0V0MncHWmQ2M}rX!2UHQi+X+G5PYkuN096dkO1AP9 zaJ^e0!ZwhN1B!qAO$A5mL=jNb9(*S36>=SrE&u5k@_qI3FB<(!L8Pry@Jvybomy7~G-hg0tWfAZh$w#peu$F%@x z{v+Apzvkxw9nA}+@azN4Mu5XBw||SWlF~ov*#Vo&xgQTKK@wQ12>|}za)dnh=$7C^ z{IV>dpz*ps(NYP_UjXtcV`8)lD0zVeu5Yeyw(i;5zl8n(7rw7dp&_Q@g5~3Y@R5+3 z_xIt#O00&>SKR{nn!3^aj(yY6T08!WLi6YN_ouDr0jR;5mKvdmsQ(Y?-MM8VYh|fT zAJ^gY5nx11ZgJbI@Tz0KUf%D69p#!e!lUKNHC!m!Yly@OTrD5WpVqG_=|?Vy-p!0x zBz*tY6XW<3N6FaAmN;?y;y!kFA$F$S=qT-GS(f{RAkiJ|eugjnadN@|b}*Y&Qw+~d zT&pUAXG@d#OkV{?o@$11LxZ&V>BQLn7egHmIkd3zA?4)qI5-c62%V}^3z{s_aLYs_ zSNbF>(rPVJ^{Q@7QW$bA^bTMG9$3CXiLWQB?Hd-dphALN3 z(ZvnX`rW+&LjF%nu;Xqt%`UH%tL%(sn|>$0;F8vP5)BL^RF}9?bKU(|M?E^|qe&yK zkMpFfl~tFWxLT!jnW z;uWQ7*g(_NZMvI?$9hP+MkYU-$|l@xI7VmWwE-5aa&|n~GCqd429W;;CULc9rbBYB zr$H-k{8XqfJ-4QCl3io_oF?X#q@2o`MkYGp4o+WX`Z9?xcqkSeOG8==#wZmY%PF;r zUy0Hy+R2_C4sboL(dace?Jwnd7PI%~dhvsFqhRG~3$0qNe0{FTgCB1jMvq_Ey{8ED z?7PvbK}V*mhVLqa%9j}J+t>#IGtOW~du&t?*k$O*98}AOZp6B0UFXlppx*T$=5UZY zsb0ZSfi_2fwaVehgsih3=P8gvD_nD6mY<(|Wad%#!(fR_bI~0!M1>>ImU2o92fH%F zcZ;VCwGuN^#>e}ni0%b*22fp92Yb&+?jH552<3s|{<t9j$u3)s36)oWTxH3`HTDnBID}A9h(Rg|Vsw)pWL}((;yoHvg zETb)SDtz~2oRuP(@5h%0PbW`ttiL47oOz6(V^o?HdYlPuiwgYK^cP^hHmEvGE$rE+ z>P}G5kJ*&AH;lOmr1$QU2g6TD#i@w{CY(Vh*7teVi|InDzCnXl#-9NRGjnm9_D?OGzF+z#^j`k_Q_{$8L zNL1v>cp@SyzK7?mLQJ56{RR5u3_UjgJeBQsqu7#Jky%(Ne&P48FIM%rEOfKx1smyh zh{x=lFdckB&f^9A#B(bW?e@(HTx8Xw#hxQYN6X-)({B{}(7I<{Hsyoc7p$LRMJf;) zABhv{1i`g48IT&8Yru%F6mnmS`c%!*{9&7jYY{P_CcgQT!#qqysBj~P);Eq zw|`hC&PI(RW{u1e$W_t4A1lMBzUw)k%;u-e&^6k=hwHw+5vt$qY5kTj83eThcRI~4H4 zT>}O)a7dsbFbG++fbQnAQrt{Th7=3pGT<1nGbm?7hER^2c={^cAjRx+4=OT!`YWz5rWjt8x; zKk1NHO9G9Z^Jw|%lLWMa%5T>c4m6HQZJLV^ zfx=TC7R=5lauR89_sXZ{`M(E#spQwDf3lDt>BO`eb1b{gg;y!(4zl5UciGcGvh67+ z589Z>Gb@NQHiZ;ck;B1IR770NcfZWg&>0m|x1f1t5Q`QVSW&|M;A;B?n`C2(D$LM7 zko*gM-WNY=(5F(sYAn9=jEA|J3lW9vt97UEDMxaM{ZBg}D_~j#X8$th)}6PBw8b zPE%a3a08jJQY@TTBcp)h`;2_i2(HQpPd8Z~kkuV}Qsw%DoG`i^>9J6$7w^Ba^#dyS zl&Jb^BXWZFJ=mfCgcreh;`jM32?iS#HFO7osL7EM2q=BX=kLa{k$k4TV@vs&2u**~ zv;&t)L+kjy8PD;HSv&$cT+$CpFu3&Gsa2=H!5M$0`gEbp-7Jz1cD6UZ>?{-O$wZae zwd%8eUw`80;2GQ4Yd^eNMX+hUG*ZbJqU8;&ac3OL3^fL6sWc;t;VTe~+jVZmMBPWTOO$nJpcz zy9XN!VD_nHFVI|uS8vMVB0UNZ4bwOW!?ed?sk zy%l@%zD6s1M238?(ov-XVnIyV7^xU+SfkNAo>S35HXO)VX*jx}AS|{vFoI7_i6Ept z9PY0Hb5D-P6y&}1ID>qojlHKV+SmI zn|^)8CI#wM@$3j_lZe}2gP&$v9v^g?5bye+T;@8Tk=}=eF=P0;wRMR$DmooMYg!}i z*;$exrOwk5!u=brBl>d;D$Y_&<@m7DVVmHWm_w4aw}*n`oB(zOEGs7N=Xq_En-yU! z(P3(8py9lVMv!4so9^)47yF9yxW`8Dk zeeLAkF~)LQf8Jv>{~nBq+Hz8I5`5>7pg#&7<+-|0SmIRXUu1k`jvbmU2i8??cP$93 z(FbivG|9SXqIO3n4j1)gyAlwv)8PWOx?~KF8aiC&&MEI2X~*DRD3q0&5j63aJ}CYQ zAWhu5_?|R=Vw*;&UBujawnG?BkkJGeh3Z%H!UM-f5m3FB)AmSx1uI*mimU2;`Uv%F zBw)IOXCf_Ob8uS9NXYNes7^fiv;w`U+m4tOzR(qXxLdq);e09M!TFa@pFkE?d_$(# zNa%E!az7r*kDH@Co9lP4iyfWyS;4{JFt7gXAigA&=!SzYQ=3Y0bU0*zUjMy!B7;lh>b3YdfIKP*Ubw>YIm z6LX(UyQr2d!hW~?PVT-lNra~sC9cbny~{%$vY#)=YQrEMG;YOuC9f*(zR2_Iv;}LU z_`36z5g{+1EJL+4v%-IL-kK~Nt9}OYd0Xpzb7s#GO224yeIJ%qi+?t=I9;SeI#YX_ zZ4aH%zJwLH&!be}_E7%lQEwZo;QN^c1}e=d42T=Nq26!mTbv4iI(f}l zj0d{-SyMtYR7&n4MkaqG_PO$97s~}m<->L&exS_hZs9LTOQ_WqT+j6A5X#fdFkVf; zK{Q%1#}r4&s&=#^W#OxPhh5D%;~;nlDDxgQz3=^kVn_NOyV$1tvGMFGBmtT%6Xy&3 zdp%Qoin26IA&jc5XH`bqS3_+o6K0khU879{I!q#1*25-2OG&-M>QbSo@PQ3+v89Qh z?gT2U4c+{<_gwBZV?Sr_pt>s{=rI*;u=X6y=+niA=rw9p5PRFCo0usF30NB9d`<&gU}Ji%CC~# zAQQ-=*D#7PPUx~)xB}ynXr9uAFBXO-CM$hKh7h2E1`BkcW+h^JagcIvNyv9iNem zM7j9ev%#TdHC% z8Qu2Ihk+`{TF5=O;wHmq&;2D0W-2B#bl^jw)fV==(uU_0-`NH?8jchgDtM9k`??1b&ua_DzWpMrtPz!w8Vc)3cu!<18>J2N1knWL1VD@6!M zb88D4(w^=E1Wj)UdUYwv?BQA|g&ij;WesNyA&_!u!@yE(v?>YN^&$Q4jACuPZ&y*% zu6LK2j8l{Afb5@b7aA8gNc79B{lK==N}tmvu6QdeThoRrvutV(SA33L4FFn3<8Du0 z7&l@yPJIpG)K{FR2ANZ=T-r7L*Z|DWXFWO`wG>l89t&{F*w`jwJ7<(da%3#ld!ng zCgfFAdL2S}-@6mZWwf~O-RjgMWAJ#<+C+HM?BoE6ePwU(2gQmZHOC~!3jzJ4+4U|q zvzQU6jaw4HKa5V@X_QKh&1|kIa%-@rgDXK7do0M5-+JSpA%~77HN6EhcMGQqUA+3- z9+#P3*#+Y(b?*0R;`{G*A1U2fo%%Y<4iq5uHE|2TWcwAj-gq(Cep`GeK(<__^|Bcl zEmzbT`ipx{<*l{U&QDFX&*;=gBywT-w#td{?Kfd(e&&U_+9)&ZQ#+|mhd=g+V(l{Grei}RgwXIz1y}ZZ2J4zt%MIU>rKE^0P@PFhrejk?+c5@0X8c_T1RD3jnMY& zRI`jtVlGgw+Avr*Yo=}iA5P6pA0i&2wjkfO3(oEr7{=_}9yCd->Z|2bk6IW}=Xmap)KaAoI2Ui9DW5-Ey?k z)EY3LPk#gK*RV4{=XlG1&c7Km>`e5ZXaGldm5VLN4jdL0;x%QZ{2wVelwedNUsfcLZIM6Ou| zV1hnO=qM^`62h{%#51H>y*fyQO**+`ui4gpQo0(ih(3+kuEZ;yb@{oB@)*J?g(J<`dv zRn3NHBw7lpy&zyJd-cF~C*Q=|Yc-=&kcWBTQ# zp6CCyYAb7eN1D^Z;c1TNC~Bxzu-Qh9XI zl?mZeeJ)o2PY$+-AVNlKZM=DniL=3*S%cDnd`pY2^&M_+C z(B9pb$0Zo&wM}6=6e@h*|C)-eq!dnOixPepu21FT*u;i>Bt3N*Q#dGSp&!Z2LdI%& z&V(Tql*t9x%S7G3(SmSuN*31P7xmNA(j1KR7UjBvr5qHcKFHZBEQl}-^)(`Y#@Gh5 zw68ex>xL8UISl?tV*GZ@yf}q{Cc z4rC@4NUErNtweccyVC0r*rw*BXEV%@!BOY-uh-|c>+gM!mx`~V9ZB^_GfR8$Nj3kf z$|{4b!+}D$rYP%}Mc7ea|8iEgw+SD|{#_9H!$y}pU9VHi3vo;~;Zhy5BV5a60DF+N zhwS^73_Bb`^GL|~vg2c_N7PzRi-*Y@)mmTMF_3cJ*X0w8ffx;&A`BaWEwBLg9TCG= z7fp2Zog-78i%SRu4N(=VF= zCHrKCnm@X$Q+QbAE~&xN(oyI4g-FpeuZ_Fi!1ai0n3H>FC|Ku<5NzAI0Ky#)R^dGC zLyTkm_TYf>llYIv%j?iiEI2%NhJ_9qVa4_Z&hhJTUE+WAvS*Z$9`i=6}y=?*-6K=U`E~UMC`boB?0Y4%44hvb_k=D+1 zDH7kM3tAy0B1g)oBeaIF7DFsUAB8etKsvr4XYQZ-(j^t($rU?Km>12+9kb7mi6Wu{ggPADQq7H|}H`0t5ucOC#2#de(&(BY?iNcO{;u5EV zGrmm44o>x7Tte&dW8V+>m@h|+R2iwP9=Z2S64CCJVYu;A*k@#i>PS}XJYU~o8IB}L@xZ|#>diypa&PdYw^CmX``S-h`z`k zQBI;Y$;AUSn_K6OpR!M`gK9$-3h|+-xwAo)xWZpFTMuv0&`H9hci=XIFrB%E{1`*U zqBm?8yJq)>!y;=RsNASR`$Ko4vU(PuJb|R#G2EXAP7t!^u}0z3NbI6wZ6Hk5M%N$Q zS>eZlSD`!{5Of+3V~?B{7gx*&v69geHAnCdV@+Nl)lzEIyAO4o+>vYHoZ1Oh%X0N7 zxav~dn0#RW3rjN%FhWU}l3np7SFVqGLu1mtAyuU7VMgp+etdfRwbo$8?keLDy!ez~vh;hW0T0H{(Y0Pw z<$O`k1n+}E7>HJaWiK>h$@u9b<;)M3869+vCc>|*x6~-wARfOC2SU4fk9!*Yj5L7akvx4&@ z30XGf6FnouxFejZ0d9up(Y{>nNzyR`^y5W!By2Qq8@0CvyZ%JRHHPQmL>4c0(6a28;iF*xI6RI)EM8`Nc+YTnBHj z_Wr?GBYt7VR$EDqVI$wuK0fkTijhOUyEWwIX}|f|ywd`AbPSpDd6I>X4m(%dpVxFe zNa)z%UABaD@-e7!xh_gGN_hpPh;QZ>49LsO z>g=zf3`$QRuoRChDqL^A>+MUgeICA5W_5s!Iqq0PFb+H&DS-+zneBuP|)caX_XiXT&?yOvt2VWtE&UQYE@TbM~s;FFf zYLaQraZ{J}WP2W54rQT~V`^(Mxq7~u_jTMJ3>EpefjR*`H;S-P4!3_v<;3|OU#ohg4b^V}<AN72ZxxNZ2{u#M0oC&?(9*)d^Y6qmk%E_&{A`FO@sE#;&ia z_z+H+1uIuvMxu2G`&^A?H^AKBUi!@WOp}DXD2!NuSHf$p1nwtlneN0H@Cv>g_&9nh zeekY5Jp?Z2Eb2v}RQ`yOSg}++jr>K-m#`QW*iF$QX;}8WHM5^E4B^*4)QIAiJ7(R1 z;|2LF<2^+#ZZ~qjxK6WAA2#aL+xm2SQKvb${FO%WBxfba=fdNh8M4A$`zeIL8A#(O zG0JP6Rx{ZL>vgF5h#qywi*h1S8qm&lpot&!qAtaO$;$YLIl?J9xrs?=k%jJ_)~KpR zJX22i$y3G1bpKsVE>F{SgsvUjf@q=96ld{p#K3SQu~KQ!fsSz)3`Y=V`sg!!pPG%? zaF6t?gSKCorc>YfeQh+y+pimQ9bcMDM4dP=2|1BWbUCh<=D1rz*iN40($46cqD5Cm zVHrc5Ow-wNj1N1~W;6@KUel|gMq58s94uFf>4i;Ok3vi?fy<@d(d?|POGMe$_svDp z|6HEiV?j(o7slKQNs@i?KJDyGqC)uY?UZ9Iba7FX;#icvE@OnoWNIKjFqu9%vy_|{ zSvdPTjZOP& zB3yQwvp#J9ASLJF%AJcM2(L4Szz9fWMV!A5oAxy{1YLkClGK@afuxEk9EDVdf?bz! z7*Sj_bso0Q`g(mKZB(uxQ2(w~sjYulkfe0S(d-|F*8z?B!~M@sN%>fK?G2LHS9BX$ z#8{z~d###O4CE}K-E8JRM$e=(4Vv_m2}zy^+jzQ%bD7z`PnX@-Yj+L%t$*9zb^4^p zQ%`$o8@WV77quF@836Gx6^Ey~8R zx2!PJSH)oM47w8Iq5_T`Z~5{|i}My&#py(cf>==%?vRF)c;nU+dk)p59!z|BGif2c zL#_K0U}#CH<%wmH*kQ4Z;M_sj0)&c(v}-3iCi}c#^ys-aHok(}oO6XJkIuuQP>LJ} zqR)s_Qg`mVOEuN$%D*7$>e)|=(kea^t1b!GA2692xhU_K{6hapWurDIPucm3=cs$JQ`+Mn)pVkBpDxM`-xXmxYG+B~x6E#Cz4- zC=ZRb_mb19Xs3F#DdZB#?LNn;IurTu;qSv`MT@SLCu~d{o4>=Rhbsy2IPZyELziAj zmltv#&bO4?%!OV{m#8vcON&wtoL32Nv5DWeF_+J=0;*i#nt1ENO0MZ^r4(|2xk}nm zm&d0@N_n?mxle+RTqP%qQg)i(VWMMVXLFi@Sap8%gu%t8Mt)P1Muqj+z4#0FjzJ_& zo>1#<6?WbR!e1BK4>8^{NC;Hr536%}<;cKgN)uk1BE7?5m;8y;b332!>?0vknQgZ=#(mbIyh`vrd-}o0bN}F?_I(32C(J z7gDo{+T?nHxW&O}6`9wWdO6p8>e|u%y18sCO~(Fs+$1PFHuZe1$kv>SirT7pl`Et_ zMn;j%8yK9J z*lyozn!0>rmK`5vW6IQ}?XR6FkC>@wUSfH|CaFSp!r*l%3!3zyITL)jN<6j?1gT$= z$}XM!{MldWNCk-y-PHJJdZ8bOS)Ojmuw{}h*~~Wy+;6u(Tz=KBfFJFf5}CT0rWR@~ z)#xKiK2s)kUgDtnmiO}I3Z+x2uUO8tv~t{l5@sUW6p=p!qXid{20D%PY?nQXWEbXT ziv$)z0oG^c9^L=);fEP80XtJDtwKCiwsLJTenau*j(Yd-xcm*5mdaJCkjdN6KY+4L z7T~o7!0c4bd;pi-VD>u`ASXv0I`QsD(%OM?Fq>@HoLMiwQ&b{ofQ8ox~Z~rNai4#qm66?8ZvjZ}XLk?nx$_PX-{%Gc zBUCGLh7Z>Y*zrT^qLJYv~}jl4*;_Vpw|G??%3?d?MWexKi1>_ zy|z8~I==u~v=91Ava=tBG{lwee>3A{0e;--vxlhPr%K+fcWmqgW}XLZH4kZ!Hj7{J z`+L@XA&nuM=lCNF#EiPP^q<6Sk?EfC3cU}Y^Y-u^4F(=N`Xp6N(KaSorPakM$PbKO6BkZFhi2wyf$1{r%xS z@I&3I&KBwFif_N;q=7+Ke~sV36OVnl{?Eby{M=@U9ab~I!hogH&u$yhIB_pmZouJ|07`N&DiF#Eg13MKY1&D8!dAmh3a;n z{`W2cG46-=L;$?%^xuFvc~Y`)GxQHVl^jgWhS{xW`^)%y==uNMC?Slv{QYn7K!)S1 z&u*!A|L5IjzQGi-GM;e)tlc-vHn*t-*rt^KJ74g6_v8P`mpFe<>VL!9?so6>mp}0z z^f#BeRawcd=ssW`KCljeoBVEq-0pq74=4kT|E@sDUBmXk5U^~(>g)$>M}x81Ie-c? zHU_-4zN~o%Ym{WU*|od;AMV~WDypSx7hMEJK|m0cj7kOp$w`t5*s#etD@lS)4iX!6 zt6L;UH$llt&Wa69ZWACNIZ0@coIzp}+f$A1@Sg8|zw@0j?!72?HsZn~(nC@FGj$lpc8X*c_JU^c^WI(MPEc&?k2PMMZ^W{^Qt zT>QjHRwg5pbhcYXF^zi=!+_01Z=3Lvntiz#2|VDk&kQH}3C`nu`L%od9hk=D>1XgK zsOh3S=gE%3a)n&qD#eFNPQ$fPM-OOtLtp{K_;j%P|(sfFE+Z1x}d7<#h&5%W^ zyB9fKgG9zDzQs29rlcVI#Wh_78YFY7GA$EEM!x=`>T>(!)tcxS^^{3%-jyoHrJil? zDMfpm4Q!c6ebdbD56B_8c@Wz^HZ9$TUGMpVi$rj&Zs)h)O$m2L`^&51x4Zl6hb%w$ zKNeL~@_4j{m2oS5nrw~1e%UC@>eb%#eqQP>db1XJU438UYkm7AzOy@aN?#(o#j_&C zWaFRT@Mn1G(!RXymhTd3t?@Y{+0>>rq3eMjsvDF(I8qx#sdu|REXImToij~tGcpLn zuhPSs@S2WH2=wxJLVu$|`h{2b?5oaV{0C0hC$U*@dqd6CwQ$c?v5m84Q)ZE++y=Gr z4ghZhQ<1a!WjLSa?p2?Zq7;jqmnYTMtFi_A`*Q_RiFGn-)qa+Pc}ClQ+;OJ0*&X^- z>ab?DQm4)MGwN>EPShsX%ai7R)2`^_U~T>u>;-=)j#Hc~`vFvfQ$eKkIN6Y%1YPL)$QGoKe|!nQyPrMCz_wKDuu@d!Sr5p$*a6F(DmiWv$RU z_}-xG{+825v8myyE|(_~BfyC225ic_Mb(?y( zDy-1h(4BC22#9%=u^k>O^rfyLfLS9haC_Ro?|bb*MR$8r{I0U^w`{nvL2X6#kmXk= z_*K5(6ONxF_7)7ohC`;}c&5=nqg#dZ!>FHqs-h#{!FB;}c@tfCj(oNiDYX%jvAXW~=??4jkuB!}nOvcK8!b2Q0wZf- zc@aM$wUymh{bjld=k#30Ug%|%4~*@7s>@Dmw`nVJD%riW>R^p2TVw0`w7mhNC*$`^ znBY&Dn7Kobn!|UWwi`qrzErh-sSk||Uek4&#qe;rSMLjk;I-m6d^UXegf?u}b=9nS zGlMt!wR(W2zYQ7kUSF@V=r40K{nGZ*RwUVng=fY6)d2akN0nc7htjmH-;gr0sKBm9 z6nmDmG+sY{YTM}B%0%Ay)F{UA_dMSCa;w)qI#gjcPANU8`G<9dDE~VD(qIvoVSYfD zWva$lP{W{rngaEW#us4*w#zr*B`aOeultd4zd<-&u!6UJWPD+E9I)( zZiU%1>D)5}4d>UdJB1CBz;Z(YH!u%}C)C`0c|%w=q1-vaQfN zKlp@?po<5EX>x3jSKRGwN6iM^Z*>am8^KR)Zqwe`b)VkWU5-J|(P2hM%w5SBifz28X8)pdofBrxmj3kpW@=nL)F&KERi@AvT>V-Ni8 zwr_!D(HkAJ zz;iUoyB9v9(xFyEZW=Jw&JypNx6><$8NgK%BXjif9R&Um71v??^$(ZY3K0^9s?9$EK zfwDHrjniT63xZc#${km8T?3|!E-g5Zw+VajE~*I#AgI!=Y|qS0`3hi~TURYp`(~!_ zal1zT&e;f;LS3&b5}{?|C2$_|ctl9}T!Id-D`9-l`S77p?x>C3t{2C&9nYP* znv)DO@~nnqM@Pk`6N=Y?^2jh>OOm?Kjw?tM_hr{`;nPj(*D}kNX=8k)-L5+n+@dLx z(i@s2?{Y_^1npexyAv5QkEClUAm60_8=B zpvl(TLb!DW4DI&XtOyvPwo_hfHr6;QZhd*IeSh49i5q60 z(9;^tb543g-hz(J3nw)e&sF`nTK$5$-H3sWnG9!O#mf+d;jx&v@p1O73I$?539*T_ z85y(ZSpAxLQ|_b`Tk{~iZTeGI5k8PZNb@gxW<- zdbE3|voiTy6yED3*L3TLpdzb$@vky@OI!KcgoLua`!oCmzR8?>rp@?i)$PtQeEW+E z7se$`FF~8Vw=1;3{K61QJ(Apg{da1lm(>V*G}@2n>##R1yuXqh6HKQ3X zl-^7`_4x7}(z0sMB2`RUt!g{8Q zW-R0Ut0~P%%Nnor_%E}*S~;~2^?Bl}TmuSef(O-90Tg3dc_yQ15L_c(N1VW5&&R!y zmlB8^{D9#p4UjS^%ffq4?_64$m^@IQc;#`uI;+1&?2(HIWio%rsew(XND6J*p1Sq;(H4;{aiG`$rp{{)dF|%xcC9?!WL@t zMfvrI+^?3BfSsHZ9BOho;T+PaBITtX;XU@TRJ# zdHBR#v$aPo0=DMX%^!-T{I+-Jdvow*5joSE5+V7rl@+Ldt3bQnJBc7ex~DOc&rmOf z_0P1}*npKdC0X^#OKtVe^Lz=Iu>51wo(nSs<1ii}gxOw-ZKh`$d~G~=YW*ealy^RN zrj9VkB(L-gkhBKRiw5Ohap=o#Yh^ZHztA5r<7F+#UwEg!idk5?`)r(h(zMP%1X9Y~ zzVH0Qar=DK={jxMPwwVXA8;+gQ9b#*HzI9xn(Cd(@8VxD%}T0b_+{MPt(5lE2a-y^ z)H8}0Q{jBJvRCDjq(JrVzGz^3`#L_q8>tu6zMxYU@pL+}n0b(&C$xL-Yk6O1ucKjb z5KlBkHTN)2-TQ~PqGhaKX5G9HD~+7F%)QGsC{`Asv-hC{k5y<;y}E3p*;@`gxWte) z-sE}{6ta&uR|6v$)3<6cF9ZwL8dKjr>?@9q@namv{BEI@-$d3dVSVm_Rq8}sWBTpr z^F0=>v=FIY_hV-kI~KEN;@sLfsi-0GY5%BcC1+esy&2d~`WM)w3nJm|>p?kni^72~ z*Y4!+?``2zs^sGN+DkKid$8B+*5}oyT(<{g-nyQ)gj$^(Htr8!(uK=ByZvb7Np0h; zD~0Y#onOkzo-n)mqvt;eVBO?+0$N2Jk1-3t__L}4g=>u%lJF=mTEFgOl;&mqnYZ3g zqhQS##z5;v{e*~hmqOkuW4D)uR5@qo{TM;uigR$1TEV5+ZQF6Qwz4wqa#_RWf!LX0CaU%M^;6w^XY04&2Wese(Hz z82BaIa?0?Pxu$lP>rSlm&K8(%V{(0^+FGix5t)X`*0P-p7BTT>J9<`Geb%-FbNmqY|ER zXg55f#vpQeO)I|CX;Ls?y6~jMTb=oR5R)%u!JF8V~s>Knka2^X457hPt2JLSHHeS0|jb|w%t5**yWo$y#`1PgGSgjalI+}2%|Q03Wfv#LIMwKJopy-}Mk zH~T+7@?m+DX5F~jdiWs$L$w`K{*3jM{c%svm;MvDsy?#<4CZ3Yn~&8C(s&s^D+Vb8 zFW;r?qPjC$c9NO{=)%w-OAtpwcw;VMNV>+;nCNP! z*Ud#r9O~cxfJ|r;x7ii%UHvdkubt(nLueK)Z`E$D6E^q()oQ)a)V57`GjNOVi8H+@ z6CXF`?gDnVJzj}?A0`>ydazOpIpv0jlL6^k{W%F#p_*Lv*zUCWu_4T5z-u8Lz#)Fn94R(T3kX3~#I@>*1#JXf_ZnD-8BUGrZSkIw3f)mLM z(<&EULhl5YBvk5SBtrdM;dsUHmJjLS-rjG z_TS*$Z5h7BFEhJ@8TAF6{f6t0n?nx>8NgdEyBerVt`;=kn*N-q7btUTu2VH%eW387 z1apq`L-%Eyz~3Ud)*Yk!!q2>0tE;!f)^Hc?I&xeexMbm51sqfXSV>?*uMV}Q(L%We zJ-pBcrcmm)0dpnD4r$@KrE{5s6}0m<-Ukbq^JD5N&bfXcYZ6|@y}fd8>*ST9`L$HQ zPQ!aP@mWPqsd2Z3 z03|Xq9ybK*HE{2Ow}{5KoPLZ>-P9D1ix3-hqbnSt3ugba&7wJ?X|Qhc$y+D!Cb!}B z8KW=1&679#lxHE?+qfStxQiTAs}9U2AhDNTuWiHm*PBJ}6f1DQLPj|kc%fSh)%fYHbpKJuT=AqxLu~j@fM9x?j&P4T8n}2Sdc@raMJ-^q`Omdkn zw-VL@-*`pu&TGAr+%pnmv}M|t*T6jz=;5MeIhJ6mQe2FEv6(RProIf04NhKaKnRaF z->I{5O+2?zpmFzHYxRAzcl8MZMqgX(;?3e-lfp<}HF?}|FlS)qGfiy1vSHO*Xs#TT zYGYQ{8}FRQ^JSWMmVB1O)VZNj>9*5WOpUz!!I`Y7m7AQ&tvl~`j>>DoKVUJxw1apzUtj)KNg{`$U-Bj=lz;tmHbNz+7$MjmbL z5i6GtS03{(c6}UYV@rMyEI&uZ?;qFvG~W90@3dwm4n4M?bTASjwFNsadESRl zdz_W56eoE;m9%g-i*r$~CnLJIseG)Jl&26Mc&Q>TKhG9sWBOEEq0RMM)~twG`CaL{ zOk=}IxN~B8ka<(AdBeP8nDGjC%2;hqN|Dd&yJ7Q=aq5ZR(PC~z(NX?Uvr<#^mgI?Y z&u(Pg_@k(+-MIB6?QXvxvBlzvGGNEBXf1@=Y(L0z1cDc@zV!(*}D z-COC#b%U0(GcH0Q#( zMBI1;v|FRPi~Rx*@@A6tou|`!tpy(K^)@!}?W>&84!%+osLD0;M^W4^+!!ZUO3b>oL(3*6xX3KqLDG4bhwM8rv5=J7eMli6e~Y#YMN~RpEvq z$?B8^F)+`JegNNgWcev;Nh>yTOVzKy}dGDT{?(ADJ6C($T($we-Wv@i!_v; zwlf|%MN#1>c1)0k?NV^ibnEhMMt8M8vYEEG4boTr7CCG&h4=f#pL|K)rh^9)T2Bx4 zAFO3-4f45Z;RguM)f_6!-AsR2daX-_chd{GV%1= z$vJUnX0*AJX=Faahb<^DHLFtah!G#m+igWH)@rLFMy4jzy#m&@n((ou}zIGUp01=qOokwSsB*-ri-hcZqvQ} z!ET~|YVd>g`0slE7R z;?+bi3XS~TUZ-9Esk=c9N*9@%bvRZoUlw1_?vRyYL)grgjvuSR3c-{h{VnjBiW=ckiOjr$o<>cGd^pzu8 zyVs=Q$8IW109HE_G9u|PS=?IyKMcyh4$O=J=dXSkzm@?qD`^a^$4RFUk zM*ZZ;CX(1c;)iZC0orYA0NxexjRYVj6ptKJUTywKTwioN3=JKJ(l@^9K}-$=z?mj@ zsQSUxKT?NGHGm24?`95xtkFfE9lhh(LJ+O@qPp^kJFoC%^x|v&MqcLz&8+=1C4!6~hudnkc&9D0#p800OxHZekhrRx1 zVe%Dgg2Pw2X;W5dPG7#WQOP!&(#7e;L+i)mkdR4 zCHE4sp6L{8y{m7`6|K?R+hvr|QzEk>^941LV$W_|VUh0ha0^M+F4f9f#_@GgpoZ|f zg;|GphOo&iT7OFcy7UW_gIvgi{zgv$e}bBE03})3OS_1+$asS#qcaaLeQngyj!zh< zt6hpeyo1C#B&sZpsX0!S1_#;N8n{A#AAzOc3Ycw~)+<`23QWuLaQAXefJc?^Pn0{xW%&6wk=NiXs`SXK z>ID&0^dM^wowYMd@rLoCG#tCQ6Oo$DBF)0|b7-34E%)UukD8U%zqyZ) zFY#H9GZu;obEe%9n%Z=mLECi_yL;_etx4hZyQlQ-PD2q4?*1an;n2uR!}BdCo@f|v zbn7daD0e*48fn4P<;-rmfE!C*IcUyRB-VN9*ztqo%!4vl$V@O%5e)q8K~w- zRC7U1kWzA>-@%}?mU|7s7I_16!=5MhmU?V^uZsDTIf$9>I|GUB$AZrJgnT}liZzFu zk?aqm527EBhD%Q0s#ek%Q{E#32TVRjYXHi;NAl5`K#P_x2eR{%3_s30m?Ncf;?VR~ znmFS1D}>|$V_^7-Jdq^%HS?4o;ePl<6AmBn`%lXepns%7v7JQV6sl9v8n)oPhtMNd z4X)LupWJ?5#Cr9BmJ-_!IOWHgN>q4nd2qsHNjgpAPaHq9Q|kc1j)^po9Rbc!TyCE7 z1!{HCiNkwcY4Qgo3(lB)h~h#0hQcissoI&F^+xG^I!yT@G+dxn5Q&b-onL7r%2yyb!MBDod&c@c18t9gEOo(cuL-J3^t z1`ICqBU-L(NMt-Zb0dS?1fl^?jKDqW`Mkf5n1id(7m}ob*F*;Cf_^hV$CQn9LvD@J^9f^u|5F8fI) zUayQ{#7FBeECz!LM0iYi#l1~5Um6G8OI3Sm7MF-pn4(H0zF@=#l5u!-z%hGx%cpQ@05=bq#Gf13>~)mbZKR6<2`u^Se?%G+Cj+b$cp6iR^b&pLb8LrTL839}-~>bjh_4b_ zKcMbZn{fI1|9^M*L?VX|;D>0}gc7619zqn%oM54ZWIuz7t!rR&VDAhHv6K&~ok>j_ z`AIC9l&`5!fq%|1ajF6``$=Hd!*ma+0kT<>vY`sps7u^#92XA*D;K$Tj2vPFG0P5V zdd2R5sWwTREYvYatN9`a7~Z4Rye*OiIu3Rl&xe%aAQ7=0;C;}kMg6%oP!~r~LbA9W zFjpv8v1G(0+^bso*9vYQQw&i@hH-(ld1nYP9Io(_QdtEG5PteIIbtfLlUqbI;wIvZ zBjssI8C&7oK!)>728^qaG-@3AF*M>zSmJ3=roT5qYY~u1BW-6VDfmaNkpkcti|64u z$yU?GmHhgf?lg{X-C()H!kWQl*Lt*xT!%z@?7l=YBe)q_e;&1+TeB`b$m)mI*55YP~uwVLJ#8~OiCV_-q8!94A&q7CV_yZF?j}3`+h8aN-nPm5X zeZ%OZzbhT2Vu!&|X5z4`K>Dbo!6yM-0K${#EI7kyQ^ky;Wv86%J0bf6*d`e$97d@y z5gZ9GH*$uVbaBVXQLmTUDtEm&w<%kK+#TigAneVWA5i)rcD1kc2gKD$vQ35EJxnGX zTKs_46(K3~V(u~RrLX57>=)WRCgA2s#{Rk^prfm@8Ptoqp?yP^etu=zsVcU{;%=^0sfob{Qc{Z>|~}opkmpkEga&kE}v6g z9woL0QaPgkc)mTs7L=!AO|S*_4>n1Z!YDPv=1VPLyO~h#niQoI`$Jri!u}Xgm zOSU58VXtzAk?9K1XDE}MVp`=y#AR7_e#n?RpA@!A8levq`%*G^6N`O; z%=OqKku&@d`@oGjFouLIOK3%V2tmpKC0B)N+pozK%;}>BNrkJwd4sa_qn?p4oE9)_ zJ;W1a2*md3g{wDOxBhyx@Bm-pBC)5P;5P_km1Ya7eiLF=!Nb)%MUyf`@tiwjeZ;O( zS5J8#{(z?c9InWtu>*zOd61jm7AWktP%ho?1%=&|7kv`PS$D>OIRuYO_InRpre7mO z^-m#r{vjk7J23ppKZpOs#JvDm>-Wi>vHd4NjvGW7mx<#_d>dp(Lq~j3KcJyspNRKi zhcZq~-@B&Kx;_i;iy_|Ezt|)?@N0a?EKB6E`hUJ04xDEosH^V+b@c^Mz~BJ!9Zsc# zF}(&Seg15kxFWMge*`ZoR(OCNy!)deCLJ#QOGFa*K-$q;QUwq4uJ9BEJM5350OKC8 zL5&f@{KEF%4j)W)QW$Uh;rc+?wFW4=&V!^EV_P?W2@4IEXdFrR@(1J^&XsnIQVvm{ z4r0t>w%d86>KHwF?BM}|AQ;YA{LjgFY+rt|5M0zmY>?zE&QtEMengyL$e$*Fl6+P$ zW595n{A&sxJ^bt<>gRi(18)bH=jL2i2k;zfZY80GhUr?Tn>h!^qi+d~U%xknlz5{==yQJV5n4l77g9IS(8tH6 zjg$!DY{gO7gjkdQy@mL4Z>!qWz_>Bw^i)sVkV}noMNOi3j9BP|wK`RPRHb>b9RPhy zIOoGDb#6)*ZQkKaGf#FY60Y|iT~ttZ2gWO0IXd8eP&htQemzNhRN8mdWrKgCMX>Dy zT7WvZg_9B=m~2`WCz4vqEc0cx*=;>%1ewa*+u!B4m}=4z=yZAz_s-JSfc`hOv-(~f zTYgLw_9*70%Od3Z6zd5KQaT)c3JsuM)K%KswC}2pLTPAE^-N8z8fTc*6WNMPK-TE3 z&F2x2BnVrY$m)H`EE#X^;lhG+-S)C+EM|H^#&GkRI*vn{oRpb~(VgNVIwg>MvU+a2 zCnuHDcS~n};rG;imz<>Bnbi169v8JiAApASrK_m%pMkxMJ zOgKK-KlmD*u|+}d%}>gHD!E2B`*~GjtFcdAqqTVSSnx|nZkoXi>f`r9`j`>G#x)Z1 ziF!aZFROp*4@q&a{V$Ct>L-~cnAAjV0%q^T%1!+qN`HM++V$ zbuXkJ3|D$1p%e%L`~Og5VW!lae5+|cAflt*8tEMbcDR6LdwX?Ng-nEG3dH%A>t{E> z_Ae1H(7gY2?CVcv?^KAgel{OmL|SU2YioN8tdE4q(rj(-`6}cEiBA96Eep;)W)Dn5IBZ9YB(c$rPx`6P?;lR{|KD!OE$7 zmbS`krVsoS5e70(sqa0!wQi*O9mWOs?!xYwJPn@urtsvV#?7gh^dcXzvN>@T2B_)< z6h@?)kGuP+=3@MYdA(cC5Z9?IC*iiIUlBL3>Z*Y$IL8|2JtJqfris%2Jt1k z4EY`8*{pek))iA|*JT`kw!=`_ELI$!qzTtiv@_RsyX%Az*bdxHlDq-&_>Yx<0Yp*HjH31#34IynR34$671xdqss;S+KHXLX2)XJ6Fltw5jGv zMc3p1*@*rJJFEA7Vx~cU3(O1+muK4LqnsWQaV6^Cr%_y9>d1Jb*B_P14aFV+Ru#ko1W1 zfy+oRX}KAzfTqVoQTN&yAjr|Iblz1yf`tbeLo0jK0Ep~c(d1$fK%G2k`8F~r;CK+{ zQoVz%($O8UA$$nuF(xVzWQ+%#J{mJ@O07{4Q`2k`*_fJ$Uk9;hRo>AhxPF6cCA=72zZ2rnW`Jw^$vNSih6kya-P^3>27@+0cUPAgf&^NNFT)q{6Xa#`ZjoADEq!qtXT?g^XV-#t@ z`#}IC{)y^FG75jDr(f3JsO{HEE#&-%De5+0Khg+Z> zv!iy3Xnp*y118DSQiz7inAQ&U`EF^L2uyK

5o}K~le6j}}>pvh$$iHU+ zq5B=!@q6orDEzq|AUAmd4cOWULJXWXv&3sjA9N@l)=`v0-U&B`XD5p|tO}$l#k)wyVcfQW7lD#s1gME(FElT^!&N8W$SYqq58|P7C-r%a{LHFGQ+(NL(R3{oomiAxm$(J(o7!?%8D3%7vXWSFfqTH`blFd<303dNDDH00IN?TXKhj!Q5PR?RC-yKI3XjKN`Z>N{jJ?J%f9(l_vSd`{KH=~Hmd^oxxVD&(IE zTI|Baxg!hnxeLi6A(b?i*QDeQE=r%KzqvRhm})qgQ+dSW1~HJTzg;DXr)&mQ?A*4# zmSjG;5KsRVJe@j=d*z0ih(i;6H{;asa}#QvJ}wd?3!7U;$H?&giV%3G{TY9yhbjME z&iFrnNy))Ru_Vwghw32&6U`27N^N>2A?*!dNXTM~4h`jpZmP?zF3*L6nG_lvb2Jsb zX^{u%m7{5ohr)T!e_JQcCWtIL7x+Px&~*y3BL(G`U0j@BByuQF_@<=SD=Jr}0SW~Q zzVvC8<1{OEk8=T;0h2=Ywo=+C;7`a{i9z8za`8m|gqwF~!EdK%yLZSr^=g(I0B81#)7bt4bcjwjS^e6lK$Dc8U@+UFCs>ydpg#dhYu@& zs74%tKShPeP#yD204)%j+L#|+1kGOd00n#$%d4Dqkf!SA*#XSYW}}1mf;odACQh44 zqOcdMsHd>X?{IZ7b`Q;jc)ZEB=Lb}(aw6lK53#?Hp4Y&@(dBy>caB%sY)>hpQ=zSXazS$Rg@Gi+9 zlC~mT9`UII)D%xuL(1dFx|@Q>4wx5g4-!=<4$fDbfw0|w`pjrKUjL})uqb;hjnVK6 zp}Q9YH<&g`FQ0lSE~j_**f8haDbmUv^YMo08)~(qR!ba4`+kH$rql0J!}=cIR5|~e z`!ub-$~mr`z)UraowZ2AiOOC-x^fB<({on*OPTk8uRQks{ybt6Y=|Eyg6KVR4pJiT zj-gn_?nw03cq>n@_J`B%sQO|p{}hpVsOf>^q9yB#3Aa8ZKaZdCkj0aam+lyS!#(p< z`F5YyONCuMyo2|J^V0B(;tcLI7%vYuQU|8k2D#?RvTXxpTtJSxB{;wI9S)bP~}>k=U5g=X_uMNud6C{N*4xfu3M2vo|T0??lDU~ zH#y63o%uyLOG2AOP&g%&9}r6YIN>%t%{QOYvq}7h=hO8Ek!iD(3b7i-G-WeotF0%s z54$4MTUt$?r9Ek0s`B^&d1zj?4arJuU)6dmqqipf==qGm=(Y2)7Y7;6ov8|l$~HE7 zB*6TZMS?yfoMc2bUmLIw+?h&?(O~+ZyJCEPwW0#2U|d`{7guk9`aNt{tR%CwC(ryt%6^aZB|bA2*qz!7Adt zOnoa{w5H4yeqj^q7A7o=30{Q94|hptWzYnDY_JH+6@2gBz4ENOu>$<+tuE$+gBwk`Lst{4m-b*EwN1rmYTZjpsvHM= z`)9BHcI)329sO5AAO@0aAHxqHUiSKuik(NtkrchJou1v{#MFjuk_Z3`X)^{)3KP-3 zErrez&3*Kdd5f5mK^_2*0{?BLO%gHcUp5G0Wj~rt7a!c~_-PM;HMaSA!};N?*_|ej z?K42bfyp^C67M@~K4Jz&!%wkoEd_@HqbG39Jtlop5&IRmkn#5>(~hhK6Pw@WGH^~w z?!BKqn!;qSM}OM9^A*%nxytYA>K+rP^5jojM54O25*!TnX++zf`x^MWaWW|*fNj+u zj{?sI&pa}d!0PX9?*(^4z|?ILd%^e)o|!%wgV}dmRiS?^dt|nWE6qdQ%4R~ zfS+GccB28ayCGn9p9SH(2Ixkc45y$U0i1%L1XYCEG1aLWYw5OoU?+?Isx0XH>xbnY zgrD;2TM+x0BV~00%UlDbsw8kXWa zK;SdSmxKjf?-r7u`sCm?2nv49I4PrDvy!X3nof{WA#o{FGMYm~?jw5SAu8?I^<3N4 z^lLw$-{wf=hO9FM(?gXbLFf$u?qbeq!s+fWfv|8jWa9EV%xL~_gD4Y801!MK4>TWi zBBJMJNPZ8oMl!0CTu-t_hfxU_D`pU;eWOAzq}|byil`?FOOn(4w}8;`WWH&g2Rj|u zLy3$NgV`IV)4wetCYADuLu7F$CSCzF@oS8lh%ErVd5H{?B#)I`{e#;BgsM*R0AW#Z zy0fv7ATdXjRZIK9`mbRj`G?roU0y5wDK=!1%=y=NaXrb#@iHpy5F`#i{ebMkxv8af zTT-#0>!7no!=-v~dyMd+lcX9x<|ofNsWi6P9?lh?jpJ-d51E`*1_2&^QgxTgqfUWv zWd@B_Q;zMDLTXEfok%q72V~6&f3Vw00snV8c3k5R=>ZTIV!4wZ zFHbwE1WKUV!!NJ2rkV`jB|D_wV6sC@7snKoC|f`i)7?@hmP#ZUFycJ8Pb;D!pF`pz zX2xfoe8s6^l+G{Zar+FR4oXx;}1PQ=b8O1sNfQ54P2j^QA6jNx2@kgQ+dDCemvQ zdeKXV4g5wh=ibCszLWLZBW%NlZ!)uY1fC8zDQ4aOWYx@FF>qT)Fk|RSaD9h8ga3OA zgXffvPL5|}qaGT?_a>|k_UQ*zq~xkLxMkT^$?3#=$h(v$P`z^>$tcD^Y-CTRchY7xm4;RrjL^NKh(NjmA2$#;xASlF3dtn zxTCdo&1$1}xQCqG=BCD%IVPB1m8G5GBHK<5sYQ~XsP{S%z@Dohp39*=j5t@GkSdpX zmF{UZu0TgS+tIhG)O%_r3%M`(svXoM5ZOabr;pLSVK6t2Hn}-kgDB_OsI~- zNa(~k#rUyR7^<>G#0b}Vp;ceqoBU)ywZS#YG%+q>X>Ex&7C@JyQ(gV!$^)kJU0}ji zoK@c&#p7Ud0z#u3+5BT!c3D-@T}`bp9|4@E;qUUH-u;Va6&21IceBjD#4tqB>F*O>BJ`(^-Ca!D7jNUqLlt_;%K-JzLB51ved7h(&NOewlJ}YBX_Q z%-b5CU4@o7xG+8_$*j6=g5&O4Lc$*^1y_C{1S>W2Onua-wog5{HB7<2#hsj&`MtU< z?rdv(gLQWIvMYMkK8dn3+ix|mMPk#YW3X}2k26$F_#yye(zxI@9tDH62%$770eD}&9hV+;3%%nwZ%mX@ZMxmYv-Ky%b2 zpS!zyTBF);PJquuJ49(v^N>f?do)7JD9O+&ieK5y zse1- ziJ`uc)L?%XZFLz}LbHOF%dOl8{PQcUn9fJMiVQ*y4UEpzgarU?J;-Q;~Fez3u z5uVS{<9~2x4RzfQ`XC-t9p~YwljXs$j8kj$&KBx+T}XDlvN>O6gqxMq2o$J?8`R`8 zbEkxEF{?{Fjogi6TT1>tm$52iM|){z!$X+m?!~w9w;uVwO;Ed?=D%)y(F|_q^yun6 zE{^LkuB&g+fyJ*ztdd<|1#||cb(zZ^tIC1vK8E!X2Zixki7y&g*h=FCj6&lqjpQ6# zSxY0%0RC0W=JghLz5<%Gcv4GY;pyR4ZPI%We^x60hcFof$&2xx^JUrvSx!1d8V41q zijN0#Wade>Qej5zO$3RBbg;NS%=x@)h+QMDlVp-3A^8LQvmgNo7E0zE!h#=XV<=)V z71HqT-qnr&?T*K!(&NJjD2}*tBgSqM7eHWrLwiA%vxB%GkgF$?P3xqODId6l<@pWX zFrOd+vXoYHq_RoY-M0)^VHT|h0ueXg1*X)p;#d7G2-9>7X+k3fD6L5EYcOP3zC5d0Pv(o9{IQ1 zA|&|_SEuG5i?!N67Hg89SpBPnk3X^cfolf<<8=s|keKWha6VuuUAI5{d<9>#j`F== z5JZfJHGwaFav}bN>JNSRCsaQoke}2lWpf#16!^GeY>1WAC*|0}>Kj4z51;eo&aaGu zjyz}=wC)cQ3eCjU=wRzy`9#vpQ0gYK%ri4wrd0#aN?4wIP_vXA1{10{Kx$*UTpe8rJl!Em`hJ$fT4d6b zOjVVSNXNY@bCVWp+{bI&y<*c|JuMvCD~9*CZ|^S=W)u``b9WIROKbZhj4IUSDk>5Of0WNu$=T>W7q~Q3HcR%r)m2ZzNsau`3=>exJqtw6Nz2c{=w`Bd`7kCi zyR%=2x^WD5pa0U;yi1=Tt<#xlFv#0ASfn_wR2ql09)@#cJg%DtbODS(&E@)L?%`1S zn&VYZcr3l=c>QjhwL(jO7%3FYdKrn_dwxp5kNo~e$@rB4E!!4+`Mym%ZfW@~N6Vq8 z+wRczR+&34R_X#>tDt43me3`y#_Si*9~v7oJ2}Zh&`ItTlIDE4nj9=Ep%~+kn9QQ1 ztuDAchs4!?z8Eh;=lCSy#+2UYmTN*nrwWBm-OoC$3KF7%LtgIU+LB8>T{#%-fQ#Y= zn))scSv@-Qxdkr8J#)hN zl3+(93Ob-f=QN z;>;FWWFYj8Q*FA%rpjFP3=8@_88%p0QEuXr zw_yPrd_U~SogB7Mi3-;O2OfOZGts2Y3`;EA_CbcOU%PcO<%Yl{R zogj~PbKGWSw3>9&AmiL zWtln6059DRzrZ`}=D>+Q@#c{tLoCcXuh7P-wq&8Lc|9xp`&cs06>m`OqIk0I)y$@T z-DZG^RonW3$uznoKE=WX-4jSVS>tAzaDR=3>6!Hz!6&@$%2O{~YG%09^1C!X_KzFk zAEjv)l01flmH>u*_OwB{)c-2(Jj0sW)_os91r}r?c;d_TmP!<%oKyG6GrycWk>^^V`3s=_m_2*h} z)`*WkF%`4TNoUe(>fW^|VQUie{Z)fl?=N28@apVoPEEJa-PP(tyDfiT6?{aL8?AQL zFkEA{v>CP3=F*|W?8}9P6&ph?|qfG^soFPg1b1snQ;E=0x1t zl?ov4P07YgyxdORtg5zXSa^r`w@Mz>)?QWU)9KKc5B_%1KeDu5MJ!mgY&yJm)GS6B z26YT?oi22ED5pni4|7Y>yqS@xkzdBG$83z7T_*UN5+=w%@}{t%T&$@5CZ0>Ft<+>B zu&4-knAn*qx~o~Q=p}gW{@Qw^v*S_cs$2abj?FjAZSK6(PV+Aaqmad<*Dczkq?ylp z9y>lnwp7jCd61|zOUlhV(ifjBkbicLWqNANdfSrd>RnNJzv7!i_M8Y*%In&|Q=vQ; zM2iTj2KS)BY?}x$qF&HtD++vGKrZoG9OL#I(s7S2*+)>uA|KsWOKoi^N&&~p4 zSrXs6{Y6kXtoJO*M^7HEBdf1KvQEdR!BMvTbNqu1UWM?N1_F*N0yQU|-Yz^Y0zE5o z$oR!8!S#$MU3X=Dg~AmwMNR6XE8#ln-h*101k*lZn^jBs#c}(yvO;_`;cqvYkuS+V!bT$1g~dctDWv+FZS{2S8u#XDG7=M-`= zbA?h@vP{3xGY|?YgD0TxJFQw57rtBKS(4DRx_P85tc!$dmV}yXz4J0Te=4$N)U;3B zRc3$sIkTLJxVoIW^Ra&HJ(l=S`wQV1OWJ@INGW=Dp41>J6YGj$jtSeE$ZQS+_!y}tFzp17y3R+z+8&HJ$qCm3FR ze9YVFaJ@=4;d+;hX2uLp6)M8UB+3_)zgHnc$&xR_`N@2*Ea?^UFO?TTX||9B<^a!O zWt-WJWz4dy+D81%2bN0C_H&ndj(vRLh&E!B+V+GVZ@q;9givv1GhJI#eBvG&2Q{%s z{;-PL(6%wDtJ4cJNBf}`5LRG|qs(T>9Ga?UjFAdItuak&de5R{yR6%1Km8!zoW0kt zO=q1l>>?xZF+UVy@mjF9Y+i^#Ip1FkrR%z@tfWI;V8Ns!MnIqF<>bRXJ0`s-n^i}X z&n#}33v<~S{xP{o7yFJ|9WWAWqPh0E*#(u9Hr~|sdFh=aK}Pk_ z`AQNq#t$vb>z?u(HF^h>I;xUkz)$4*8NZ1hj|7Q=HBtn2xNavATkR** z3e#D=c%O#D^vvP6%x($S3+U$V%`xVi1U9EWenoS)(c`!&2G`UsR-p7b%@!qm(<{Z;mW0pT5*{@hItLS}wLJMJj1m-M?yB zy6NWObByPnIYCnSSCv{|>j9;9Ud?b(xQKJc9G73C6dMQBCy?2|hEqS?MNxQ_OUq)o zVB5tga%Io1U}WbVvAqUr^M*0!-1|!c?zcX@)|Fi^XV$T~E$wI%iybU&v8h4Bge2j@ znk@>IY$94${(>By<7)R18!YRKH)^)XwTR4W+-sWlsIH!~z=dg)5y!;3ORcmWAG~>W zKXtRe#4uoU>lq2>(A z?-K5+x$0cjESQwfoklm#mqtMCUKPu8snokD7_coAGo)O3n*3<5vU>bzVz0*QBs!*y zVeum zKYM!YQyeB+(|w+7Ax`^wH|44E+RGA0Et-y-?@%7F!U>6s!|%uRMrGlOoes0{JY{LKD3SMKXHRFq^1yEEY)S> zR`uWghg1fP*t>sFWq%wWpFe(3ET{t06j8~W`T03`n(I+PrOlDoyTDoiys;zz3B4J=kwRnKP}36)86KciI``3hpnwTOuETmRa{N+nZaU z)~x&8)IrwF)sw{KV1qSKE|EqNdAA$6@ExQs%#fFP`}dq7=G&?Ug_U6K51c}W$G(7c zug+u>l4;z)z!Dr~GpVQnDn^_k5}&3ho7yI^_<(-~ zqp_^L!jm~UbM?r|--Ay*1lj0lhz4>}WNLp$RlR9qlSUM8%Vg)amJLxjI(3j4IstmC zGnw3|RXE{kl+Jg%u?tJol&)3WyVV8iVmzm|wl~!_A(#AlfQ~w-t+B?h*o9KK`oDln z)lz%q_cBhjY5W2w;!}Y9UNLPAy~gD4gKtdArStyI-EYzeBMs<5Muroj7(isYwg3wL zj;yBTd=FG@1gV_=W+Q1n{REZaA7hrDC`E$W0`1kS+-9RmPc)6tMj>>?Dh9NKrGf=h zs7);tIC6<;#&FY6KSALG=e1~lR+^jf@)?|&e1oF zu?Wf3zzey?qjwx$|^Flt6hJt@cVzONIQSC?}qw=OYz$vB=a)l3S#|A ztoV`=Ew6OPjp^%33m(oT!JI#u-!1PatLh!JslS=s-fM0I*{IbD&%wFb_`)E~9%4md z_p)>Cx`(_`^Mf~M?cO$vX06z$%uQabE^>*N)hLpvZs*fQE){n&w;w5VxRb~;ckroG z?zs3`wAzqxPOKmF%17Eu1?8LpV|BH#h=qxBxZK4;5nJ1NS(F?4gx$!y_wlJEN&C~f z6NaU%i2S49me%Dv2F}DAT9=!aa#)S(q%WcjzBFj~?#E4AabJIgoUO6Bd~gIk)UonI zyvq9aW&X&ULJs3^ZkoP^kgt&NPtnnjROhyQSe@?YY-J+6`!g>q4xW~O)Q2C*(AtZC zYV5t?lxho~IFCD}O=r6>)FW9TezykONN^^l8D=_9Wjt72Cg<=?=pu?se3L|WzmB&u z8}^{*iW%BV-?KZ*?F43t-*qW{rF?CD42=*HVt;RX4m08(aJ zqts`OC5f=i2^<=#WAy8z>G-{Zp)xN*g%_7|(4-G(?_GY(p2nH|I^y)T2PCWG2{&86 z=l4q@Ft=}n`yWl{GM0sBNMrWr6qA9fxu>Zj^Jt zn56e(lnSGCB~lY(#^YEVZ@fMfTM+-`X+lNzL19l?d{*@{SSd?dl)cDc^k$+`D<*g6 z$C_!u%MuSYGd4D3dC}T>rM~$jR{ygzJ}wqUQ3L6DoDn*hc{4(HwM~i}-xKZOe$ABe z9fp&o+w&rB53Iy`%I75B-HZAu+Oi$z@R3IrrTfBDn?l3*Oiie4D zeU*cD;&pEnbmMxKEHW=@c7_MIjz*aA^f8@Fy1q)S*79WHTw+T_T-n7u_&KA+(2hQb z<^-L`W%sMs9kI`kzp}@lVOK1Z@pa>BlUm`RYzTE>iffj%qD*}g(ZxsCMHE$92rcjR zJ&+0cBE`FFSU+Fm$oM&UK8& z+RFM1qABmeeItm+?Puc1kKmahCWD>X%uSltZGb+AzNy7{NBg@=^yricy3!mAz_A33ogl&IbA=;`=_nhfQYdgpwcO2j7 zDi}`O`}X|Zc@)EJjs|5(j^o^d~@#pF>l%D z<9>wkN@cjDUmw)U0DiWz1YX*Mlp)Fy(vxBZd2U`$;E9NW*X)WWaz$eY-(lwPjgN|O z1*EWVzF1`;G{-b83UIW1B2ME~@}-Z*GZ0=PHUyuYkM3BJo}Inx)V|zb6D@{d@-{c^ zN3@EfNbV>!T!3IROI}j*!H>`RUK}5-N+(!|J30zaS_tPK;$@I}dHxJtf}2i2tYH_;7lJ*Y;}I|TmrdHTN~A!g+kYun>Z*7%`q3l5OdwAkVF9tBv5Jf`Fy zZo|`fU3?b(r@jY@=V1cX*4pfkZ$)1D(tiAG9C7B6b{c<~gj+_7#;3JntqQcBke9KH zo(*aMuwL{6%dMWrsLkLAjq4XyV3V6mY5Du;ClQgb_Xj;Enth2Xav6zMOouw}e7Tt{ zBL*YlIyBEQ3zmpkb<$~{sJHoa=fu2RSZ9KEGy78y5Cz*t;E%ShQ&LHu)4KZT&R&a(}sGTS0=rmEhlI`AraMoOVKx)@1`{(jAiP3{jRcB z-=K$j5N0LcRqQg3xxpqrdQ})NkY>D{>d~rW=p9g+bnK`S(Zfbd(%_ZOLQ!qYdMQ4W zt&Xss!*&+|Zgxj$WQO3`)k6<9!e2r;-6Rcfpp|!E2*Q!V-4T5+MRZXmuXjiJ0KN{P zWgI0}MR5d(=7tDwOddmNi@!?2s+V3XRH#4IFfwtcX|OESs5p4)Mm>}-CRjwkT$9I~ z9N|fMO}BqunXH2leMUOfVU9BACX7 zfuxdcGk#4=1YAfK(dmt)ZRc?Q zMu-vfC6mi5_^fW|wYv%Q$%J*Fm8N7lUTLpj7zp3SS5N44BL`fxa_fgvPv^7Sj1Dyl zkP?htdJBpLR&=iP&h;uDd+BW+T|!XCR(b+3uP?N)Y@GOh<89x>jqP&pLkyB?9ZlNZ zXRLJu?Jw=T)i1hsd+;o9_55#6DYZ-5LIdkuqVD}d85QfmlO6BppwPAt@s>p`5gf3qBd&i{~$B zMF-%*M`j^fiu_MBZ)IcB&Eq^Bp<6+FIm&vf@g7n6uJWsm%dcGY$v;$gS%{Cx>W+&A*~aA@=!6$5B;?!?^`TW zu8N;U(PNp$iHWHspE-|6{Yww)=ir(PTtpEMO_e^=0fb*b9j>xo(p8hF)T$@>Xu>F| zPN-u>A$dx^zoB5#C6@7r?m^^T*4*01f<_Je)mSu#uF5M$d*{>dI3!`|xTvHgFK*mi zqYkP74Zb#MX%-@?CW+-81haHjvaawqv9C@Cd0n_(xDw~|+Lh-p0@f#Xu~sjf zaji=rqyw$1d=hE@qNkYqqg!Jx`kL}J{=**}d!Q)n%(lJ0w3`L5Ls`aPa&vBRN1lkl zfa9T&O8GSOlCcS1n?q-X*_=W!OORU8EbTbcE7UzzT<|Kq-sr&w?sI~~fjj?*d|IsB z=jimEk|smP2gTmjcf0*>>ZkSUux2+T!aESn;O7Z$l_+1=R03~ym>zbyGCAStsMk8# zkq*ZURi^eC>!!+PetuVuU`rN!>%Aejb6i}I{ujfGJ0gjnHf{v^aKHNgb6#r)LmF-| zF4t;U?A_LrY*%S_E3Dt2e-b9nDzIs~&<_fkEQ}OM{YL&Sgu3+-`?qMtkv^;JUO4&K zsOURf5_?59LF5ugiXQ)Q=RD?;_a$^P;hlGs^jdXLLogB5c$_GS}QES?p#Z2n4HRSR1QSvXwOSOM1{h7}M_n~bS%AD!Q?1LtEr(RziiCLe# zC@5(1(6L<>aq?7w+;BBi)mTq5+Cv{R=TxGv<8opI$I|C=DIJ!G8OK?;BkZ(0k3Edu ztgjA5Ti|BX^bMaT`30!dD4uB-!OK{uC5b0JEdTDB{r;*O)JG-80UzzEN(S6#}=}&FOu2r&(U8@EmnhzK!tv6T&!esMk!&oqwua4{-aP8 zPLH3UAKBQ?cWL&jr zl|KrzyKGR+E6^ftc-=mN?jcOUtmavgcB)&tvV+5&NLhNW8wu)&y*EtKQr^qN2iPCwTpKtNJc7Nmp-WRR~3`!T{x>VP{3&EeRdOphM(e zH3u~C{%#EbElGrPJk;O_ouWPnM`(aZk>Z<0RaFqUVeJf>`>F2UsLBLN#;ro%?W?zf z7=@>L;mUhHX$3WaEvB~-8pN1f6xFr3zT}`LR4FVr_AbK>;^LteCMBDH9#FLR`R61{M6E!*_ zXg-reoxx!@`vK4+03tT6k5mI2fi-@ke3vw}|8aARS|>`}@C1eats%7IF-*Y*ZhnyC zC?)m698gYL!8Lb|w;52_K;^37{Xc`TriH#+poWT&e-DnNrw5%vUk=IQyLstXGuw7! zuQFGAE)4_OknXl1A`n^ub8L>wrblk8$Nejk^c9G`BwEom&w^RCRhoPSA*b@Ft)=fk z_UG@(EeASeANBE=EX<*Xjw*xNMyWGwj-v{CdtKuKo3DM{{X$cpxzA(|x-N7k$V4&cHJ?@G{81%B41!V+MB>>Z)AsT-|=%E^2+nx&KDRn@?uC zx$Strywe1YWxc6WTrTq}T8Hxx$iXzwX{au?g!~n=GTtbeQD<{89`BhikhzWUx_bhn z(9QpWQ>(xEl^9>iD_)lZ%6Zz=H>VX9^ass)SRgCQdA+usx%DV-8>x%4yEPN!$nsy` zD~CF7H0dH+ga4yE6m#gmoX3-G2fl)!G8B9z41iRS6~5zW2k38f5eE0(dm%+9*}mN7{*=bG|8%d3#N1QefMsqm`E z`{TMJJ?>Au8{CktW2uTbLS7xF(q~jQv8Gwvy4({pNOmpi7;Y9D*^qveWu0X9`Vkqi z5}qX`%76FXbB=f~Tut6X`}-3Xu0+R|n5D+dy((hbXOM~5_%b0xKilDT4w$H+Vnom7eQWg;n zs-K?b_kB=PxXp7|EFrl;_CpIBkF==ALZ5m3&xw=-#8eaZ9MD)60v~@D*&T zzD&`tgB8y zDMsU*;S~e9hc>q2eCn4ZE3d?qa2jDRha^=;fu_E2Jx$wnqx3hX&KvugNUNGYcxgwL ze1CjcZb19!C1Y%mi00i=(}y)RD0hmVI?B8$(ny}HtBa;|9WCnQ?m9g!*mRl?mwQu3 z6M-o1JPX}PY`U($asEETJt=JmFuaisaZe_|*?2A%OH)1A65YMvGSlT^EH<2rxr|d6DB*c}7?lmEbW!ni_gNC1T zZ8!Z|&2n_w#R_J0P>KEU1&c&6As(o{#ZMM|URAYeiCKMPmK4?~qE}^tEq)MYq#JZE zI)NhQyX{|GaW94>woM?g75!*%*U1sY8i_YM+}7}wG#4}1dZ_Npcl3;sl$VIzPfrX5 zF+eKB#s`u3Q}YVho8Z^{OVT3b;;Tu~X|$b2 zuqeTPc3pQ{<4W@BF`Gr7nq)m>(qei~r2JQtOB`J$YW60~?Q`gR(fH+Ic^lKaMom_> zis|RPT`svtq>>O~14i|R#%@WgNolw_eF>bG2&_;x(#O4-@T1ayq`L)X-IGrVXTfrQ zyuT&4d%HZ~tw4N$o9NLvp3@4F(H&6M>h6c!7vkb(P`B1Kn#Ii0ZPF5mN+ZtmNLvfu z=b6=Je4{`BdU~)>^A&tKH!1zid_u*Xi;KAj$s%DuHEOk4PKd0!VyU@|kC4AnU;DI; zDXCP&>U@MYm;}urlCKsmlpy-4>Y6AF%9wvWe9$uHD5jC(3Nv1CvF(#dn8q2$3JfF+ z5aRKw!o?LICDbl5L-ead@cl-lv}o|;%Z~o4X}1y9D}r&v+G(< z)HkEA5AdDXft$^qmb`=<5tj+YDRt;hMS39eyXI^gf+Vb&w-Hj_aLral>&T2w%TZ2` z+9GFSvxgQ;n`rDdD%9CcFT~LvU@$_i4Bv9?d~W3qXR$-*o)>@$ddZGxdkRXOTjF{# zvYqmUMs|NBcel-n$ATTdR@WifZS}RMqFhBiNN(KM=V{|^ySfv5Q;hj2i<4{Np6$)i zB?E6063GIPO*&^}J^zB7n(Xvd$nm&1_6WOchyD2Mqs%LTCM`nv(R(zWYdOj**(g8F4e9ji$=Q6#an`@MwP z=4ilZ(3)kw!8g&>_$=LZi{zfCCcz>b&e&O8r3w+&-(X)%gbGx;JRj*>C~q}Xc7-2u zba!+2_fqp^-tqG^pd)LXORJI&ZIoA*@-xgg5>w?jN!ChMe_v3p&=+s;>iTv}pLu-5 z=%obSGSg8Xr(bzj79EY)ew&3*LY3>Y#}xqhd15K-0QL3mnI=k^L) z^j1W+0^F@9N+L)Ft7~S3M=G~%bl?mArN#38QX+~$*5j#W{D|TX_?WHZv1nIU=RR*^ zpZ-)46fyPL;A)us(bg;xe^))VQJ;tvtJ2)viVC_H6S_~@`HLVrB*UE3R|>2jnC>s= z$c_x<=y}3Tk%|U3Y4zrmvShgjDF(!R&d6!QkKIdo+n(RsFE;I3R#>%<5H9&#Upq`D zOPE9|IFgS@FBmW`^uT9%i%P;;%jO&c;RmeVU2p-D~T$b_$$^mLu-C0 zeTia0v7&k*6N&@+rbZux0suS${N8mq3 zHx~e8kGwKhUNIKgZ^A0CyN?P~aK#b`R-MPwVyofF%(uxGmIq;4f!D`%t35+_rSIsl z-0;SRAf- z!p+Wglyhn6Sq2EezBH9mCKie7O2h6^856xm^$nH6pX#CkHrDgfHaTt@W}dxCSE5NtdGtp9FfQ4(u>&y z<(CERqPLyAatDh$7AIK<6+5uZx<|1V5mP`b#@;!}_0%`M6y=!x8c{L1S+TL#0v%>a z53injYe8C^(+TR{EQTd+*B4$hH?FDCUzAkzx687UR15aVkg&OICebUJXrsuhm_N`s z1{<$xu3un{tNllL{4mo=1#y!~OO@b$xGeZg;wm&N#McI0DmQj>JWrC!Ijhlyw%5ns)V9Y zCGcU0pMyf*fHq$r)n7coQTf|v+g2Y3-F<0=woT7Njv!UGmjl76rnZ2CqT=%g{Jr*I zlAzBtZrcWgB1^vh_G4&kRQ)o+IY3e!_y<(&Nw-Nw`V}GjC7}HX{0P+(ki!IS|82Da z$k|!1y;!Q?+8-~?et!#y($}u;a6&k!$@J$TY(D?EDfGaJI@UMdQg`L@!}*_K6wrJu z;F|Jir@xXC2J|(DrDs+_OPU5qk<*QT2;BhPT1;y|Y^?-8Ksj?6WpeGfKIxgqV&|>0_ZXXdju4|Z`{$%!@FbZf56=0K|-CG#@3HG06JH;n(^UtjQejWV6AM7FO z|8~Xb1!u-yq-hY2EkaMKh{}H}m@j+fKXP~wiyvEE8oJeo`wvv_99#f` zHXsBbKSVmmlyN!u5vYUEJlu6n--f;iR4@Flx!@cB@R_<7;4Yx?U=W(uZ=T=*u>~5B zlLkE*^lMDu+Rl5y@9ui#DMR@aB@wqDcD`yzHvACxeEry+AG(JZH7_Q_N1@@Ra$9<; z{Wj0j^p~`7dY?U_{JZ*i9F za>CW0%++Y)TWRj-&Gl>rZ48@^4~xYgY@w?sHNV2wTl!;wUC`IAf2nFY?TMb3g{{j@ z?*m4O2Z-{nli_EyXWQQ7#j065Dv*U=sho-~jmI*6*%2-TljvV!` zYtz1u?br4m&O;$8@PQ-D&abuI8Q13cu`gN<*D>@l;2+ws8cGL-cG3m4+(aXR zF)!?o1k9^>vr!1+Ba`(iF@C9O{ta38DR^B<&9LaoqEA4(rCPCbS>|??9g6!%tUZEL z;{^6}%PFsDX5m32$G;%Ai*l|S39W@df~UKc{VDT9YqLH>YyCu@iOUiSz3^OSiWkhX z7+%;AuVY)R5I@nKtII9hudBhDaBA$rY?%zk#r^F!+8BD4)q`La!5x7{W}8!GNu9sy zgVWpw@dGV`4I+qgTMd8IO3>%(2lrGS1jLepD-_7yIVqBxG2c$Vf981pH9TTA84GJv zT>t5f+i}>L&i6O4eSxCnecB%M`H4-UR3`)u%{X;d8<;?`-0}_Np`y{>f`X!b)O4wBT#4*_UDH^H$x4M`b}?I;?VDD| zUnMQNy7~%5GrEd>;JR6)K^rlrd+0(6D?R&t_IL6M&9cD=d8t*QJmZzri76~pBDX){ zz`U&ACKV1N#vON7aCS@@Qd(YditThwdz54OK7@9U%=LVf)jQ0>2p42~nAy5FfeQ{e zG5$4Uh@JZS#(h%1-Z*P+e-*_naj0Vm>MJHDfrS=e5C1sGm>nB~x-qujW3k$KgLy6y za@autS1t$A z>D7JCf`cqkz)TZE+NC>EsXiiIX;(qJjX#8O%Cyp}koz2R1|dGXgBf=1obViLP}O+nT5*X`w$oZ1m@{X;5@esA4nNv@>oMdi8060Jba$r@7zXol z^ehs~w#=7`mHL(k58Y$9E)G9s#ECfFii!ip5~tR~DW>a?oW=3XDx|DiX@bqn#od;g zX^Meak(I6|Hn9%o2layrc25>v%O4s2EI4pVgB7%`EIHonb%^J_(q_i^7vu*YT*?d8 zc7r$y6I)zGQV^5f6nnoWr&68vMsh(iIk&&vTSD)U4@2**QUT<#Dka7;Cc}jdoLVc*Q8hLvLO4;M|bkQaUMuAy?1T=@v9dm zFOLc^#$WF@D=4ws-t>yxiX2&~qZ>pumJ1@2&^kIq4BFVn7@9cKTgHdBM2`|329$j*C%FJ%9Dk!cl0zA}B;NJB$ zQ(J=+kkY{Cuc=!MuKbEQYrZ&b#Bz6_UhI&~+xA%L(5NRrxjrdBU?k;B+J)(H_UoM^ zjBcVRS-vh?13dK<15#c`Rtae8zq#<{4BQ)0?|CXq-a)&|*{7^fwlttw_LG(a{e{mG z@tTPQL_w-&Z-=gdkcj-ei0T;ksEh72tZ-08(?)eDE@Csv;>MM)R%hZ_ZjdfzM5cfA zIK42$-xWN5r9!k~)qlxHY>SlO>#*<0>(w~!>314Hd0-t12g`PH7P;{ViTFVmD~#bE zE0l)yT67bM*`&C?8n#E?k*XF|S1wGJ+P z2lbRD9w(W4{x+HpcPZ`uZJ!*{O?jy1x$xT_lAyl*x5;+M-TEV^L58w>Y)Y`Z>UY{} zzkk-7UjRdZ7ary_3Dgb3KwICR*I_@yfRc)~M3+_Ghr*M=FMveZcUmn%SZoGp-=x|O zCp=E7{qo}d2(lXx)h;FV%bo{6sMed$XwJc};7N@=XId`Tl&3e3XJ26Z8I}ze$V@t= z_U99bRZ8$brqXc_hTk>^wQxuaFXLMVc0_26$pT2a9aZZ;x!RPWPPIqqJWgu&WH7BU zftC5d5 zVU+b<1w!xuouwK^)r<~HZ-@n|qCJ-(39moR`T(MrJ2*M;E_kSuWvICAEA?qN8n_s@ z08k|E}5Krd{8n;cneS)SteJH1OAt1=)v98to>hjpre!u$t!t;b{$hD=k?2<-_Q_n zfeA|=&l4761GuSlT6aJUS@cvIoz%xgTzfN9^UTuVkZf!A6V+?%ax?{H(#qUG<<6VS zfd5}eFy&bZ%t2}kzt$`62}PC9d;$xA4N?rYJz2MMmn3zd0w_J0&;uLE=GBzu zpJWKXOHarfTpfflD@k$^Xby7k7YptE0-zR|;I&bRIu7{WS^xa!(f{Ey26-oC+M%V; z(Djz@OnY0Kd?9SR#s)i^b)Z8WboI?(A;B)-Dyk*hQM&B*ya84Rsy^Shbg&co7bNBj zEw|+`WR!?{sCK*YC{P4x`pxBWxv4()uS015r&UO8W3o0Dyg+++%U_T-XPVMd70COc zr9%z@Z$OoO4Hm#n(7~Vchxa$w{(^94)BTTAJF{!KfnUa3LU`-hz^wxV;*oa$FuPhUeu+A>Fio$eL9vqmkJs5F&bCr ze^WHF|3MjAp8&X$LyWON79VnohW9s&EJPueG6SXwRO=tg05v=+iAM5>FbxYMQ+753 z;_YbgKNPa7^NY?9h{Jv`V35(oZ-Or|-<^MB(a`a)fm!_~mN3%)h8cR&qd_gI;<_Bo zC7RHy5IPpfizB_8U|N4@G^e=0lk(4#IE2b9kyZ?%0}UgO>wf_7AYZ}>AdTJ>yh#tl z_fD!|A=2!7+F(gkbN*MhKi7NyZzP!t&od%0Cxu{5}k%n z;Vi=Jd*C`S3-8$=%~}8VSYTXxEsDV3*-)|yq#0BYE&G-)kD;20TShZ(OJOy z#Arf6V!sv;h76_Eo`hV$)4{<{)*lQFT~;ymf>N19kRt|E_FH(!M)H2!f%}(3nBRy> zy5EE$XFAh!DkrQBxfNUw3Hc(!r;|&KNvs^{S?}Lu^Y)}jUp)!Wv{wC2- zmrT9mBMcR_4|c%q#sgFUsWhdx_G`R7A@p=q%Ft^7qcOaa0JMatUnN6ELr8WIl_Hn@ zho@wCg(@?oo}NG>d1MuMmq-5KXc!>W*#U7cF6~`#;_t3knZY^_-;oXikw9>7L>vJ; J8v4IR{uh)3jt>9; literal 0 HcmV?d00001 diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Lab.LineNotify.Service.TestProject.csproj b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Lab.LineNotify.Service.TestProject.csproj new file mode 100644 index 00000000..4e97586f --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Lab.LineNotify.Service.TestProject.csproj @@ -0,0 +1,26 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + + Always + + + + diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs new file mode 100644 index 00000000..da5eee85 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Threading; +using Lab.LineBot.SDK; +using Lab.LineBot.SDK.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.LineNotify.Service.TestProject +{ + [TestClass] + public class LineNotifyProviderTests + { + [TestMethod] + public void 發送訊息和表情() + { + var provider = new LineNotifyProvider(); + var response = provider.NotifyAsync(new NotifyWithStickerRequest + { + AccessToken = "3lZwryen62tiQ4BKfh3uH3NFoFtALF4SrfgLWMIKrXh", + Message = "HI~請給我黃金", + StickerPackageId = 1.ToString(), + StickerId = 113.ToString() + }, CancellationToken.None) + .Result; + Assert.AreEqual(200, response.Status); + } + + [TestMethod] + public void 發送訊息和圖片() + { + var provider = new LineNotifyProvider(); + var response = provider.NotifyAsync(new NotifyWithImageRequest + { + AccessToken = "3lZwryen62tiQ4BKfh3uH3NFoFtALF4SrfgLWMIKrXh", + Message = "HI~請給我黃金", + FilePath = "1.jpg", + FileBytes = File.ReadAllBytes("1.jpg") + }, CancellationToken.None) + .Result; + Assert.AreEqual(200, response.Status); + } + } +} \ No newline at end of file diff --git a/Line/Lab.LineNotify/Lab.LineNotify.sln b/Line/Lab.LineNotify/Lab.LineNotify.sln index 114055d7..461679d2 100644 --- a/Line/Lab.LineNotify/Lab.LineNotify.sln +++ b/Line/Lab.LineNotify/Lab.LineNotify.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.LineNotify.Service", "L EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.LineBot.SDK", "Lab.LineBot.SDK\Lab.LineBot.SDK.csproj", "{0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.LineNotify.Service.TestProject", "Lab.LineNotify.Service.TestProject\Lab.LineNotify.Service.TestProject.csproj", "{8355BA66-8285-407B-B8D4-3208E66B2D6B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0EA7AE1F-ECF9-4BC8-BA95-CA2F8FA1E95F}.Release|Any CPU.Build.0 = Release|Any CPU + {8355BA66-8285-407B-B8D4-3208E66B2D6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8355BA66-8285-407B-B8D4-3208E66B2D6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8355BA66-8285-407B-B8D4-3208E66B2D6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8355BA66-8285-407B-B8D4-3208E66B2D6B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From fb1402293b55993ea3ebaac3c23f1235bae71416 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 15 Jul 2021 18:57:47 +0800 Subject: [PATCH 113/267] add test case --- .../LineNotifyProviderTests.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs index da5eee85..fd937ee7 100644 --- a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/LineNotifyProviderTests.cs @@ -9,6 +9,16 @@ namespace Lab.LineNotify.Service.TestProject [TestClass] public class LineNotifyProviderTests { + [TestMethod] + public void 取得AccessToken狀態() + { + var provider = new LineNotifyProvider(); + var response = provider + .GetAccessTokenInfoAsync("3lZwryen62tiQ4BKfh3uH3NFoFtALF4SrfgLWMIKrXh", + CancellationToken.None).Result; + Assert.AreEqual(200, response.Status); + } + [TestMethod] public void 發送訊息和表情() { @@ -31,12 +41,22 @@ public void 發送訊息和圖片() var response = provider.NotifyAsync(new NotifyWithImageRequest { AccessToken = "3lZwryen62tiQ4BKfh3uH3NFoFtALF4SrfgLWMIKrXh", - Message = "HI~請給我黃金", - FilePath = "1.jpg", - FileBytes = File.ReadAllBytes("1.jpg") + Message = "HI~請給我黃金", + FilePath = "1.jpg", + FileBytes = File.ReadAllBytes("1.jpg") }, CancellationToken.None) .Result; Assert.AreEqual(200, response.Status); } + + [TestMethod] + public void 註銷AccessToken() + { + var provider = new LineNotifyProvider(); + var response = provider.RevokeAsync("3lZwryen62tiQ4BKfh3uH3NFoFtALF4SrfgLWMIKrXh", + CancellationToken.None) + .Result; + Assert.AreEqual(200, response.Status); + } } } \ No newline at end of file From 8c1c30d61d07d74f6c8b3fef75a5a08a80be53c5 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 19 Aug 2021 22:44:44 +0800 Subject: [PATCH 114/267] refactory --- .../Lab.LineNotify.Service.TestProject/Tests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs new file mode 100644 index 00000000..28c3c767 --- /dev/null +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs @@ -0,0 +1,13 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.LineNotify.Service.TestProject +{ + [TestClass] + public class Tests + { + [TestMethod] + public void AAA() + { + } + } +} \ No newline at end of file From 0abe6564b4b22e45cd3cdc11e27c38f1028bc395 Mon Sep 17 00:00:00 2001 From: "yaochang.yu" Date: Thu, 19 Aug 2021 22:51:40 +0800 Subject: [PATCH 115/267] ss --- .../Tests.cs | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs index 28c3c767..8282edaa 100644 --- a/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs +++ b/Line/Lab.LineNotify/Lab.LineNotify.Service.TestProject/Tests.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.LineNotify.Service.TestProject @@ -5,9 +7,91 @@ namespace Lab.LineNotify.Service.TestProject [TestClass] public class Tests { + private readonly Dictionary> _pool; + + public Tests() + { + this._pool = new Dictionary>(); + this._pool.Add("info", this.GetInfo); + this._pool.Add("status", this.GetStatus); + } + [TestMethod] - public void AAA() + public void GetInfo() + { + var key = "info"; + var response = this.Get(key, "yao", 18); + } + + [TestMethod] + public void GetStatus() + { + var key = "status"; + var response = this.Get(key, "192.168.1.1", 1024); + } + + private TResponse Get(string key, string p1, int? p2) + { + if (this._pool.ContainsKey(key) == false) + { + return default; + } + + var func = this._pool[key]; + return (TResponse) func.Invoke(p1, p2); + } + + private InfoResponse GetInfo(string p1, int? p2) + { + return new InfoResponse + { + Name = p1, + Age = p2 + }; + } + private InfoResponse GetInfo1(string p1, int? p2) + { + return new InfoResponse + { + Name = p1, + }; + } + private StatusResponse GetStatus(string p1, int? p2) { + return new StatusResponse + { + Code = p2.Value, + IpAddress = p1 + }; } } + + internal class Content + { + public string TypeName { get; set; } + } + + internal class StatusResponse + { + public int Code { get; set; } + + public string IpAddress { get; set; } + } + + internal class StatusRequest + { + public int Code { get; set; } + } + + internal class InfoRequest + { + public string Name { get; set; } + } + + internal class InfoResponse + { + public string Name { get; set; } + + public int? Age { get; set; } + } } \ No newline at end of file From a003ff28ca45abd24da4798e2642c7146d9c03a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Thu, 19 Aug 2021 22:59:54 +0800 Subject: [PATCH 116/267] Delete DI directory --- DI/Lab.MsDI/Lab.MsDI.sln | 49 - .../App_Start/DefaultDependencyResolver.cs | 31 - .../App_Start/DefaultDependencyResolver2.cs | 35 - .../App_Start/DependencyInjectionConfig.cs | 44 - .../Mvc5Net48/App_Start/FilterConfig.cs | 15 - .../Mvc5Net48/App_Start/RouteConfig.cs | 20 - .../App_Start/ServiceProviderExtensions.cs | 27 - .../Controllers/Default1Controller.cs | 28 - .../Controllers/DefaultController.cs | 43 - DI/Lab.MsDI/Mvc5Net48/Global.asax | 1 - DI/Lab.MsDI/Mvc5Net48/Global.asax.cs | 24 - DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs | 40 - DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs | 30 - DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs | 9 - .../Mvc5Net48/Message/IScopeMessager.cs | 6 - .../Mvc5Net48/Message/ISingleMessager.cs | 6 - .../Mvc5Net48/Message/ITransientMessager.cs | 6 - DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs | 14 - .../Mvc5Net48/Message/MachineMessager.cs | 13 - .../Mvc5Net48/Message/MultiMessager.cs | 13 - DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj | 202 - DI/Lab.MsDI/Mvc5Net48/NLog.config | 45 - DI/Lab.MsDI/Mvc5Net48/NLog.xsd | 3644 ----------------- .../Mvc5Net48/Properties/AssemblyInfo.cs | 35 - .../Mvc5Net48/ServiceScopeHttpModule.cs | 42 - .../Mvc5Net48/Views/Default/Index.cshtml | 18 - .../Mvc5Net48/Views/Default1/Index.cshtml | 18 - DI/Lab.MsDI/Mvc5Net48/Views/web.config | 42 - DI/Lab.MsDI/Mvc5Net48/Web.Debug.config | 30 - DI/Lab.MsDI/Mvc5Net48/Web.Release.config | 31 - DI/Lab.MsDI/Mvc5Net48/Web.config | 51 - DI/Lab.MsDI/Mvc5Net48/packages.config | 16 - .../App_Start/DefaultDependencyResolver.cs | 40 - .../App_Start/DependencyInjectionConfig.cs | 40 - .../App_Start/ServiceProviderExtensions.cs | 27 - .../WebApiNet48/App_Start/WebApiConfig.cs | 23 - .../Controllers/Default1Controller.cs | 27 - .../Controllers/DefaultController.cs | 45 - DI/Lab.MsDI/WebApiNet48/Global.asax | 1 - DI/Lab.MsDI/WebApiNet48/Global.asax.cs | 14 - DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs | 28 - DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs | 9 - .../WebApiNet48/Message/IScopeMessager.cs | 6 - .../WebApiNet48/Message/ISingleMessager.cs | 6 - .../WebApiNet48/Message/ITransientMessager.cs | 6 - .../WebApiNet48/Message/LogMessager.cs | 14 - .../WebApiNet48/Message/MachineMessager.cs | 14 - .../WebApiNet48/Message/MultiMessager.cs | 14 - DI/Lab.MsDI/WebApiNet48/NLog.config | 45 - DI/Lab.MsDI/WebApiNet48/NLog.xsd | 3644 ----------------- .../WebApiNet48/Properties/AssemblyInfo.cs | 35 - .../ServiceProviderDependencyResolver.cs | 50 - DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs | 42 - DI/Lab.MsDI/WebApiNet48/Web.Debug.config | 30 - DI/Lab.MsDI/WebApiNet48/Web.Release.config | 31 - DI/Lab.MsDI/WebApiNet48/Web.config | 54 - DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj | 186 - .../WebApiNet48.csproj.DotSettings | 2 - DI/Lab.MsDI/WebApiNet48/packages.config | 17 - .../Controllers/Default1Controller.cs | 35 - .../Controllers/DefaultController.cs | 44 - .../WebApiNetCore31/LogFilterAttribute.cs | 27 - .../WebApiNetCore31/Message/IMessager.cs | 9 - .../WebApiNetCore31/Message/IScopeMessager.cs | 6 - .../Message/ISingleMessager.cs | 6 - .../Message/ITransientMessager.cs | 6 - .../WebApiNetCore31/Message/LogMessager.cs | 14 - .../Message/MachineMessager.cs | 13 - .../WebApiNetCore31/Message/MultiMessager.cs | 14 - DI/Lab.MsDI/WebApiNetCore31/Program.cs | 26 - .../Properties/launchSettings.json | 30 - DI/Lab.MsDI/WebApiNetCore31/Startup.cs | 49 - .../WebApiNetCore31/WeatherForecast.cs | 15 - .../WebApiNetCore31/WebApiNetCore31.csproj | 8 - .../WebApiNetCore31.csproj.DotSettings | 2 - DI/Lab.MsDI/WebApiNetCore31/Worker.cs | 22 - .../appsettings.Development.json | 9 - DI/Lab.MsDI/WebApiNetCore31/appsettings.json | 10 - DI/Lab.MsDI/WebApiOwinNet48/App.config | 14 - DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs | 8 - .../App_Start/DefaultDependencyResolver.cs | 39 - .../App_Start/DependencyInjectionConfig.cs | 34 - .../App_Start/ServiceProviderExtensions.cs | 27 - .../WebApiOwinNet48/App_Start/WebApiConfig.cs | 23 - DI/Lab.MsDI/WebApiOwinNet48/Commander.cs | 15 - .../Controllers/DefaultController.cs | 32 - DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs | 69 - DI/Lab.MsDI/WebApiOwinNet48/Program.cs | 18 - .../Properties/AssemblyInfo.cs | 36 - DI/Lab.MsDI/WebApiOwinNet48/Startup.cs | 19 - .../WebApiOwinNet48/WebApiOwinNet48.csproj | 111 - DI/Lab.MsDI/WebApiOwinNet48/packages.config | 18 - DI/Lab.MsDI/WinFormNet48/App.config | 18 - .../WinFormNet48/DependencyInjectionConfig.cs | 64 - DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs | 61 - DI/Lab.MsDI/WinFormNet48/Form1.cs | 21 - DI/Lab.MsDI/WinFormNet48/Form1.resx | 120 - DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs | 7 - .../WinFormNet48/Message/IScopeMessager.cs | 6 - .../WinFormNet48/Message/ISingleMessager.cs | 6 - .../Message/ITransientMessager.cs | 6 - .../WinFormNet48/Message/LogMessager.cs | 9 - .../WinFormNet48/Message/MachineMessager.cs | 9 - .../WinFormNet48/Message/MultiMessager.cs | 9 - DI/Lab.MsDI/WinFormNet48/Program.cs | 26 - .../WinFormNet48/Properties/AssemblyInfo.cs | 36 - .../Properties/Resources.Designer.cs | 71 - .../WinFormNet48/Properties/Resources.resx | 117 - .../Properties/Settings.Designer.cs | 30 - .../WinFormNet48/Properties/Settings.settings | 7 - DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj | 113 - .../WinFormNet48.csproj.DotSettings | 2 - DI/Lab.MsDI/WinFormNet48/Worker.cs | 25 - DI/Lab.MsDI/WinFormNet48/Workflow.cs | 16 - DI/Lab.MsDI/WinFormNet48/packages.config | 8 - DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln | 31 - .../App_Start/DefaultDependencyResolver.cs | 39 - .../App_Start/DependencyInjectionConfig.cs | 67 - .../App_Start/ServiceProviderExtensions.cs | 20 - .../WebApiNet48/App_Start/WebApiConfig.cs | 25 - .../Controllers/DefaultController.cs | 36 - DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax | 1 - .../WebApiNet48/Global.asax.cs | 17 - .../WebApiNet48/Message/IMessager.cs | 7 - .../WebApiNet48/Message/LogMessager.cs | 9 - .../WebApiNet48/Properties/AssemblyInfo.cs | 35 - .../ServiceCollectionExtensions.cs | 26 - .../WebApiNet48/Web.Debug.config | 30 - .../WebApiNet48/Web.Release.config | 31 - DI/Lab.MsDIForAutofac/WebApiNet48/Web.config | 58 - .../WebApiNet48/WebApiNet48.csproj | 184 - .../WebApiNet48.csproj.DotSettings | 2 - .../WebApiNet48/packages.config | 20 - .../Controllers/DefaultController.cs | 30 - .../WebApiNetCore31/Message/IMessager.cs | 7 - .../WebApiNetCore31/Message/IScopeMessager.cs | 6 - .../Message/ISingleMessager.cs | 6 - .../Message/ITransientMessager.cs | 6 - .../WebApiNetCore31/Message/LogMessager.cs | 9 - .../Message/MachineMessager.cs | 9 - .../WebApiNetCore31/Message/MultiMessager.cs | 9 - .../WebApiNetCore31/Program.cs | 22 - .../Properties/launchSettings.json | 30 - .../WebApiNetCore31/Startup.cs | 49 - .../WebApiNetCore31/WebApiNetCore31.csproj | 20 - .../WebApiNetCore31.csproj.DotSettings | 2 - .../appsettings.Development.json | 9 - .../WebApiNetCore31/appsettings.json | 10 - DI/Lab.MultipleImpl/Client/Client.csproj | 23 - DI/Lab.MultipleImpl/Client/UnitTest1.cs | 128 - DI/Lab.MultipleImpl/Lab.MultipleImpl.sln | 28 - .../NET5.TestProject/AutofacStartup.cs | 53 - .../Controllers/AutofacController.cs | 38 - .../Controllers/DefaultController.cs | 30 - .../Controllers/FuncController.cs | 33 - .../Controllers/MultiController.cs | 56 - .../Controllers/UnityController.cs | 39 - .../NET5.TestProject/File/FileProvider.cs | 14 - .../NET5.TestProject/File/IFileProvider.cs | 7 - .../NET5.TestProject/File/ZipFileProvider.cs | 14 - .../NET5.TestProject/FileAdapter.cs | 19 - .../NET5.TestProject/FuncStartup.cs | 63 - .../NET5.TestProject/NET5.TestProject.csproj | 28 - .../ServiceProviderExtension.cs | 37 - .../NET5.TestProject/Startup.cs | 41 - .../NET5.TestProject/UnitTest1.cs | 214 - .../NET5.TestProject/appsettings.json | 10 - DI/Lab.MultipleImpl/Server/AutofacStartup.cs | 63 - .../Server/Controllers/AutofacController.cs | 35 - .../Server/Controllers/DefaultController.cs | 31 - .../Server/Controllers/UnityController.cs | 33 - .../Controllers/WeatherForecastController.cs | 39 - .../Server/DependencyConfig.cs | 35 - .../Server/File/FileProvider.cs | 14 - .../Server/File/IFileProvider.cs | 7 - .../Server/File/ZipFileProvider.cs | 14 - DI/Lab.MultipleImpl/Server/Program.cs | 25 - .../Server/Properties/launchSettings.json | 31 - DI/Lab.MultipleImpl/Server/Server.csproj | 21 - .../Server/ServiceProviderExtension.cs | 14 - DI/Lab.MultipleImpl/Server/Startup.cs | 52 - DI/Lab.MultipleImpl/Server/WeatherForecast.cs | 15 - .../Server/appsettings.Development.json | 9 - DI/Lab.MultipleImpl/Server/appsettings.json | 10 - DI/Lib.MsDiForScrutor | 1 - 185 files changed, 12859 deletions(-) delete mode 100644 DI/Lab.MsDI/Lab.MsDI.sln delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Global.asax delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Global.asax.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj delete mode 100644 DI/Lab.MsDI/Mvc5Net48/NLog.config delete mode 100644 DI/Lab.MsDI/Mvc5Net48/NLog.xsd delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/web.config delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.Debug.config delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.Release.config delete mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.config delete mode 100644 DI/Lab.MsDI/Mvc5Net48/packages.config delete mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Global.asax delete mode 100644 DI/Lab.MsDI/WebApiNet48/Global.asax.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/NLog.config delete mode 100644 DI/Lab.MsDI/WebApiNet48/NLog.xsd delete mode 100644 DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs delete mode 100644 DI/Lab.MsDI/WebApiNet48/Web.Debug.config delete mode 100644 DI/Lab.MsDI/WebApiNet48/Web.Release.config delete mode 100644 DI/Lab.MsDI/WebApiNet48/Web.config delete mode 100644 DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj delete mode 100644 DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings delete mode 100644 DI/Lab.MsDI/WebApiNet48/packages.config delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Program.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Startup.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/Worker.cs delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json delete mode 100644 DI/Lab.MsDI/WebApiNetCore31/appsettings.json delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App.config delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Commander.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Program.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Startup.cs delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj delete mode 100644 DI/Lab.MsDI/WebApiOwinNet48/packages.config delete mode 100644 DI/Lab.MsDI/WinFormNet48/App.config delete mode 100644 DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.resx delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Program.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx delete mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings delete mode 100644 DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj delete mode 100644 DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings delete mode 100644 DI/Lab.MsDI/WinFormNet48/Worker.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/Workflow.cs delete mode 100644 DI/Lab.MsDI/WinFormNet48/packages.config delete mode 100644 DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.config delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/packages.config delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json delete mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json delete mode 100644 DI/Lab.MultipleImpl/Client/Client.csproj delete mode 100644 DI/Lab.MultipleImpl/Client/UnitTest1.cs delete mode 100644 DI/Lab.MultipleImpl/Lab.MultipleImpl.sln delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs delete mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json delete mode 100644 DI/Lab.MultipleImpl/Server/AutofacStartup.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs delete mode 100644 DI/Lab.MultipleImpl/Server/DependencyConfig.cs delete mode 100644 DI/Lab.MultipleImpl/Server/File/FileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/Server/File/IFileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Program.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Properties/launchSettings.json delete mode 100644 DI/Lab.MultipleImpl/Server/Server.csproj delete mode 100644 DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs delete mode 100644 DI/Lab.MultipleImpl/Server/Startup.cs delete mode 100644 DI/Lab.MultipleImpl/Server/WeatherForecast.cs delete mode 100644 DI/Lab.MultipleImpl/Server/appsettings.Development.json delete mode 100644 DI/Lab.MultipleImpl/Server/appsettings.json delete mode 160000 DI/Lib.MsDiForScrutor diff --git a/DI/Lab.MsDI/Lab.MsDI.sln b/DI/Lab.MsDI/Lab.MsDI.sln deleted file mode 100644 index fa493253..00000000 --- a/DI/Lab.MsDI/Lab.MsDI.sln +++ /dev/null @@ -1,49 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30503.244 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormNet48", "WinFormNet48\WinFormNet48.csproj", "{FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiNetCore31", "WebApiNetCore31\WebApiNetCore31.csproj", "{08C0CD22-F32B-4E54-BC60-AB6912C8D84F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNet48", "WebApiNet48\WebApiNet48.csproj", "{429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mvc5Net48", "Mvc5Net48\Mvc5Net48.csproj", "{91B2AC8D-1516-4D84-AF08-D72A50854607}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiOwinNet48", "WebApiOwinNet48\WebApiOwinNet48.csproj", "{680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Release|Any CPU.Build.0 = Release|Any CPU - {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Release|Any CPU.Build.0 = Release|Any CPU - {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Release|Any CPU.Build.0 = Release|Any CPU - {91B2AC8D-1516-4D84-AF08-D72A50854607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {91B2AC8D-1516-4D84-AF08-D72A50854607}.Debug|Any CPU.Build.0 = Debug|Any CPU - {91B2AC8D-1516-4D84-AF08-D72A50854607}.Release|Any CPU.ActiveCfg = Release|Any CPU - {91B2AC8D-1516-4D84-AF08-D72A50854607}.Release|Any CPU.Build.0 = Release|Any CPU - {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Debug|Any CPU.Build.0 = Debug|Any CPU - {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Release|Any CPU.ActiveCfg = Release|Any CPU - {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {49F395E2-6467-4209-955A-D361369FCEA6} - EndGlobalSection -EndGlobal diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs deleted file mode 100644 index ab391905..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; - -namespace Mvc5Net48 -{ - internal class DefaultDependencyResolver : IDependencyResolver - { - public object GetService(Type serviceType) - { - if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) - { - return scope.ServiceProvider.GetService(serviceType); - } - - throw new InvalidOperationException("IServiceScope not provided"); - } - - public IEnumerable GetServices(Type serviceType) - { - if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) - { - return scope.ServiceProvider.GetServices(serviceType); - } - - throw new InvalidOperationException("IServiceScope not provided"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs deleted file mode 100644 index 0336d89b..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; - -namespace Mvc5Net48 -{ - /// - /// Scope 的生命週期錯誤 - /// - public class DefaultDependencyResolver2 : IDependencyResolver - { - private readonly ServiceProvider _serviceProvider; - - public DefaultDependencyResolver2(IServiceProvider serviceProvider) - { - this._serviceProvider = serviceProvider as ServiceProvider; - } - - public object GetService(Type serviceType) - { - return this._serviceProvider.GetService(serviceType); - } - - public IEnumerable GetServices(Type serviceType) - { - return this._serviceProvider.GetServices(serviceType); - } - - public void Dispose() - { - this._serviceProvider?.Dispose(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs deleted file mode 100644 index 2c1a5ccf..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Linq; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Mvc5Net48.Message; - -namespace Mvc5Net48 -{ - public class DependencyInjectionConfig - { - public static void Register() - { - var services = ConfigureServices(); - - var provider = services.BuildServiceProvider(); - - var resolver = new DefaultDependencyResolver(); - ServiceScopeHttpModule.SetServiceProvider(provider); - DependencyResolver.SetResolver(resolver); - - //config.DependencyResolver = resolver; - } - - /// - /// 使用 MS DI 註冊 - /// - /// - private static ServiceCollection ConfigureServices() - { - var services = new ServiceCollection(); - - //使用 Microsoft.Extensions.DependencyInjection 註冊 - services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly - .GetExportedTypes()); - - services.AddScoped(); - - services.AddTransient() - .AddSingleton() - .AddScoped(); - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs deleted file mode 100644 index 921340fc..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Web.Mvc; - -namespace Mvc5Net48 -{ - public class FilterConfig - { - public static void RegisterGlobalFilters(GlobalFilterCollection filters) - { - filters.Add(new HandleErrorAttribute()); - - // filters.Add(new LogFilterAttribute()); - filters.Add(new LogFilterAttribute2()); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs deleted file mode 100644 index c951f437..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Web.Mvc; -using System.Web.Routing; - -namespace Mvc5Net48 -{ - public class RouteConfig - { - public static void RegisterRoutes(RouteCollection routes) - { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - routes.MapRoute( - name: "Default", - url: "{controller}/{action}/{id}", - defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } - ); - - } - } -} diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs deleted file mode 100644 index a346c1b6..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; - -namespace Mvc5Net48 -{ - public static class ServiceProviderExtensions - { - public static IServiceCollection AddControllersAsServices(this IServiceCollection services, - IEnumerable controllerTypes) - { - var filter = controllerTypes.Where(t => !t.IsAbstract - && !t.IsGenericTypeDefinition) - .Where(t => typeof(ControllerBase).IsAssignableFrom(t) - || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); - - foreach (var type in filter) - { - services.AddTransient(type); - } - - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs b/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs deleted file mode 100644 index 567997d6..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Web.Mvc; -using Mvc5Net48.Message; -using NLog; - -namespace Mvc5Net48.Controllers -{ - public class Default1Controller : Controller - { - // GET: Default - public ActionResult Index() - { - var single = this.Resolver.GetService(typeof(ISingleMessager)) as ISingleMessager; - var scope = this.Resolver.GetService(typeof(IScopeMessager)) as IScopeMessager; - var transient = this.Resolver.GetService(typeof(ITransientMessager)) as ITransientMessager; - var content = "我在 Controller.Get Action\r\n" - + $"transient:{transient.OperationId}\r\n" - + $"scope:{scope.OperationId}\r\n" - + $"single:{single.OperationId}\r\n" - ; - Console.WriteLine(content); - this.ViewBag.Message = content; - var logger = LogManager.GetCurrentClassLogger(); - logger.Trace(content); - return this.View(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs b/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs deleted file mode 100644 index 3f17c0d1..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Web.Mvc; -using Mvc5Net48.Message; -using NLog; - -namespace Mvc5Net48.Controllers -{ - public class DefaultController : Controller - { - private ITransientMessager Transient { get; } - - private IScopeMessager Scope { get; } - - private ISingleMessager Single { get; } - - public DefaultController(ITransientMessager transient, - IScopeMessager scope, - ISingleMessager single) - { - this.Transient = transient; - this.Scope = scope; - this.Single = single; - } - - // GET: Default - public ActionResult Index() - { - var single = this.Single; - var scope = this.Scope; - var transient = this.Transient; - var content = "我在 Controller.Get Action\r\n" - + $"transient:{transient.OperationId}\r\n" - + $"scope:{scope.OperationId}\r\n" - + $"single:{single.OperationId}\r\n" - ; - Console.WriteLine(content); - this.ViewBag.Message = content; - var logger = LogManager.GetCurrentClassLogger(); - logger.Trace(content); - return this.View(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Global.asax b/DI/Lab.MsDI/Mvc5Net48/Global.asax deleted file mode 100644 index 9fa1c945..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="Mvc5Net48.MvcApplication" Language="C#" %> diff --git a/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs b/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs deleted file mode 100644 index e8693b77..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using Mvc5Net48; - -[assembly: PreApplicationStartMethod(typeof(MvcApplication), "InitModule")] -namespace Mvc5Net48 -{ - public class MvcApplication : HttpApplication - { - public static void InitModule() - { - RegisterModule(typeof(ServiceScopeHttpModule)); - } - - protected void Application_Start() - { - AreaRegistration.RegisterAllAreas(); - RouteConfig.RegisterRoutes(RouteTable.Routes); - DependencyInjectionConfig.Register(); - FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs deleted file mode 100644 index 8738a24c..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Diagnostics; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Mvc5Net48.Message; -using NLog; - -namespace Mvc5Net48 -{ - public class LogFilterAttribute : ActionFilterAttribute - { - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - var serviceScope = filterContext.HttpContext?.Items[typeof(IServiceScope)] as IServiceScope; - if (serviceScope == null) - { - return; - } - - var serviceProvider = serviceScope.ServiceProvider; - - var transient = serviceProvider.GetService(); - var single = serviceProvider.GetService(); - - var scope = serviceProvider.GetService(); - var scope2 = DependencyResolver.Current.GetService(); - var noeq = scope.OperationId == scope2.OperationId; - Debug.Assert(noeq); - - var logger = LogManager.GetCurrentClassLogger(); - var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + - $"transient:{transient.OperationId}\r\n" + - $"scope:{scope.OperationId}\r\n" + - $"single:{single.OperationId}"; - Console.WriteLine(content); - - logger.Trace(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs deleted file mode 100644 index 76e285d4..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Diagnostics; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Mvc5Net48.Message; -using NLog; - -namespace Mvc5Net48 -{ - public class LogFilterAttribute2 : ActionFilterAttribute - { - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - // var transientMessager = filterContext.HttpContext.GetService();//失敗 - - var transient = DependencyResolver.Current.GetService(); - var single = DependencyResolver.Current.GetService(); - var scope = DependencyResolver.Current.GetService(); - - var logger = LogManager.GetCurrentClassLogger(); - var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + - $"transient:{transient.OperationId}\r\n" + - $"scope:{scope.OperationId}\r\n" + - $"single:{single.OperationId}"; - Console.WriteLine(content); - - logger.Trace(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs deleted file mode 100644 index e3bda957..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Mvc5Net48.Message -{ - public interface IMessager:IDisposable - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs deleted file mode 100644 index 3946da29..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Mvc5Net48.Message -{ - public interface IScopeMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs deleted file mode 100644 index 78d712f3..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Mvc5Net48.Message -{ - public interface ISingleMessager : IMessager - { - } -} diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs deleted file mode 100644 index b83ff166..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Mvc5Net48.Message -{ - public interface ITransientMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs deleted file mode 100644 index b40b1319..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Mvc5Net48.Message -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(LogMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs deleted file mode 100644 index 8d91ec1d..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Mvc5Net48.Message -{ - internal class MachineMessager : IMessager - { - public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; - public void Dispose() - { - Console.WriteLine($"{nameof(MachineMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs deleted file mode 100644 index d2206ff8..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Mvc5Net48.Message -{ - public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager - { - public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; - public void Dispose() - { - Console.WriteLine($"{nameof(MultiMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj b/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj deleted file mode 100644 index 543d462b..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj +++ /dev/null @@ -1,202 +0,0 @@ - - - - - Debug - AnyCPU - - - 2.0 - {91B2AC8D-1516-4D84-AF08-D72A50854607} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Mvc5Net48 - Mvc5Net48 - v4.8 - true - - 44362 - - - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - true - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - - - ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll - True - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - True - - - ..\packages\NLog.4.7.6\lib\net45\NLog.dll - - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - - - - - - - - - - - - - - - - - - - - ..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.Deployment.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.Razor.dll - - - ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Helpers.dll - - - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - - ..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll - - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - - - - - - - - - - - - - - - - - Global.asax - - - - - - - - - - - - - - - - - PreserveNewest - - - Designer - - - - - - Web.config - - - Web.config - - - - - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 60289 - / - https://localhost:44362/ - False - False - - - False - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/NLog.config b/DI/Lab.MsDI/Mvc5Net48/NLog.config deleted file mode 100644 index cd5f89eb..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/NLog.config +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDI/Mvc5Net48/NLog.xsd b/DI/Lab.MsDI/Mvc5Net48/NLog.xsd deleted file mode 100644 index 32246a71..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/NLog.xsd +++ /dev/null @@ -1,3644 +0,0 @@ - - - - - - - - - - - - - - - Watch config file for changes and reload automatically. - - - - - Print internal NLog messages to the console. Default value is: false - - - - - Print internal NLog messages to the console error output. Default value is: false - - - - - Write internal NLog messages to the specified file. - - - - - Log level threshold for internal log messages. Default value is: Info. - - - - - Global log level threshold for application log messages. Messages below this level won't be logged. - - - - - Throw an exception when there is an internal error. Default value is: false. Not recommend to set to true in production! - - - - - Throw an exception when there is a configuration error. If not set, determined by throwExceptions. - - - - - Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. - - - - - Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. - - - - - Write timestamps for internal NLog messages. Default value is: true. - - - - - Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. - - - - - Perform message template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. - - - - - - - - - - - - - - Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). - - - - - - - - - - - - - - - - - Prefix for targets/layout renderers/filters/conditions loaded from this assembly. - - - - - Load NLog extensions from the specified file (*.dll) - - - - - Load NLog extensions from the specified assembly. Assembly name should be fully qualified. - - - - - - - - - - Filter on the name of the logger. May include wildcard characters ('*' or '?'). - - - - - Comma separated list of levels that this rule matches. - - - - - Minimum level that this rule matches. - - - - - Maximum level that this rule matches. - - - - - Level that this rule matches. - - - - - Comma separated list of target names. - - - - - Ignore further rules if this one matches. - - - - - Enable this rule. Note: disabled rules aren't available from the API. - - - - - Rule identifier to allow rule lookup with Configuration.FindRuleByName and Configuration.RemoveRuleByName. - - - - - - - - - - - - - - - Default action if none of the filters match. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. - - - - - Ignore any errors in the include file. - - - - - - - - Variable value. Note, the 'value' attribute has precedence over this one. - - - - - - Variable name. - - - - - Variable value. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events that should be processed in a batch by the lazy writer thread. - - - - - Whether to use the locking queue, instead of a lock-free concurrent queue The locking queue is less concurrent when many logger threads, but reduces memory allocation - - - - - Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch - - - - - Action to be taken when the lazy writer thread request queue count exceeds the set limit. - - - - - Limit on the number of requests in the lazy writer thread request queue. - - - - - Time in milliseconds to sleep between batches. (1 or less means trigger on new activity) - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - Delay the flush until the LogEvent has been confirmed as written - - - - - Condition expression. Log events who meet this condition will cause a flush on the wrapped target. - - - - - Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events to be buffered. - - - - - Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. - - - - - Action to take if the buffer overflows. - - - - - Indicates whether to use sliding timeout. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Maximum queue size. - - - - - Maximum current connections. 0 = no maximum. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Indicates whether to keep connection open whenever possible. - - - - - NDLC item separator. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Renderer for log4j:event logger-xml-attribute (Default ${logger}) - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Option to include all properties from the log events - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Viewer parameter name. - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - Enables output using ANSI Color Codes - - - - - The encoding for writing messages to the . - - - - - Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). - - - - - Indicates whether to auto-flush after - - - - - Indicates whether to auto-check if the console has been redirected to file - Disables coloring logic when System.Console.IsOutputRedirected = true - - - - - Indicates whether to use default row highlighting rules. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Condition that must be met in order to set the specified foreground and background color. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - - Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. - - - - - Condition that must be met before scanning the row for highlight of words - - - - - Indicates whether to ignore case when comparing texts. - - - - - Regular expression to be matched. You must specify either text or regex. - - - - - Text to be matched. You must specify either text or regex. - - - - - Indicates whether to match whole words only. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to auto-flush after - - - - - Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - The encoding for writing messages to the . - - - - - Indicates whether to send the log messages to the standard error instead of the standard output. - - - - - Whether to enable batch writing using char[]-buffers, instead of using - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. - - - - - Indicates whether to keep the database connection open between the log events. - - - - - Name of the database provider. - - - - - Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. - - - - - Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. - - - - - Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. - - - - - Name of the connection string (as specified in <connectionStrings> configuration section. - - - - - Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. - - - - - Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. - - - - - Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. - - - - - Configures isolated transaction batch writing. If supported by the database, then it will improve insert performance. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Text of the SQL command to be run on each log level. - - - - - Type of the SQL command to be run on each log level. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Convert format of the property value - - - - - Culture used for parsing property string-value for type-conversion - - - - - Value to assign on the object-property - - - - - Name for the object-property - - - - - Type of the object-property - - - - - - - - - - - - - - Type of the command. - - - - - Connection string to run the command against. If not provided, connection string from the target is used. - - - - - Indicates whether to ignore failures. - - - - - Command text. - - - - - - - - - - - - - - - - - - - Database parameter name. - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Database parameter DbType. - - - - - Database parameter size. - - - - - Database parameter precision. - - - - - Database parameter scale. - - - - - Type of the parameter. - - - - - Whether empty value should translate into DbNull. Requires database column to allow NULL values. - - - - - Convert format of the database parameter value. - - - - - Culture used for parsing parameter string-value for type-conversion - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Layout that renders event Category. - - - - - Optional entry type. When not set, or when not convertible to then determined by - - - - - Layout that renders event ID. - - - - - Name of the Event Log to write to. This can be System, Application or any user-defined name. - - - - - Name of the machine on which Event Log service is running. - - - - - Maximum Event log size in kilobytes. - - - - - Message length limit to write to the Event Log. - - - - - Value to be used as the event Source. - - - - - Action to take if the message is larger than the option. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether to return to the first target after any successful write. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - File encoding. - - - - - Line ending mode. - - - - - Maximum days of archive files that should be kept. - - - - - Indicates whether to compress archive files into the zip archive format. - - - - - Way file archives are numbered. - - - - - Name of the file to be used for an archive. - - - - - Is the an absolute or relative path? - - - - - Indicates whether to automatically archive log files every time the specified time passes. - - - - - Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: - - - - - Maximum number of archive files that should be kept. - - - - - Indicates whether the footer should be written only when the file is archived. - - - - - Maximum number of log file names that should be stored as existing. - - - - - Indicates whether to delete old log file on startup. - - - - - File attributes (Windows only). - - - - - Indicates whether to create directories if they do not exist. - - - - - Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. - - - - - Value of the file size threshold to archive old log file on startup. - - - - - Indicates whether to archive old log file on startup. - - - - - Value specifying the date format to use when archiving files. - - - - - Indicates whether to enable log file(s) to be deleted. - - - - - Indicates whether to write BOM (byte order mark) in created files - - - - - Indicates whether to replace file contents on each write instead of appending log message at the end. - - - - - Indicates whether file creation calls should be synchronized by a system global mutex. - - - - - Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. - - - - - Is the an absolute or relative path? - - - - - Name of the file to write to. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Indicates whether concurrent writes to the log file by multiple processes on different network hosts. - - - - - Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. - - - - - Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). - - - - - Indicates whether to keep log file open instead of opening and closing it on each logging event. - - - - - Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write - - - - - Indicates whether concurrent writes to the log file by multiple processes on the same host. - - - - - Number of times the write is appended on the file before NLog discards the log message. - - - - - Delay in milliseconds to wait before attempting to write to the file again. - - - - - Log file buffer size in bytes. - - - - - Maximum number of seconds before open files are flushed. If this number is negative or zero the files are not flushed by timer. - - - - - Indicates whether to automatically flush the file buffers after each log message. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Condition expression. Log events who meet this condition will be forwarded to the wrapped target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Windows domain name to change context to. - - - - - Required impersonation level. - - - - - Type of the logon provider. - - - - - Logon Type. - - - - - User account password. - - - - - Indicates whether to revert to the credentials of the process instead of impersonating another user. - - - - - Username to change context to. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Interval in which messages will be written up to the number of messages. - - - - - Maximum allowed number of messages written per . - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Endpoint address. - - - - - Name of the endpoint configuration in WCF configuration file. - - - - - Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) - - - - - Client ID. - - - - - Indicates whether to include per-event properties in the payload sent to the server. - - - - - Indicates whether to use binary message encoding. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Name of the parameter. - - - - - Type of the parameter. - - - - - Type of the parameter. Obsolete alias for - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether NewLine characters in the body should be replaced with tags. - - - - - Priority used for sending mails. - - - - - Encoding to be used for sending e-mail. - - - - - BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Indicates whether to add new lines between log entries. - - - - - Indicates whether to send message as HTML instead of plain text. - - - - - Sender's email address (e.g. joe@domain.com). - - - - - Mail message body (repeated for each log message send in one mail). - - - - - Mail subject. - - - - - Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Indicates the SMTP client timeout. - - - - - SMTP Server to be used for sending. - - - - - SMTP Authentication mode. - - - - - Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. - - - - - Port number that SMTP Server is listening on. - - - - - Indicates whether the default Settings from System.Net.MailSettings should be used. - - - - - Folder where applications save mail messages to be processed by the local SMTP server. - - - - - Specifies how outgoing email messages will be handled. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Max number of items to have in memory - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Class name. - - - - - Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Encoding to be used. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Indicates whether to keep connection open whenever possible. - - - - - Maximum current connections. 0 = no maximum. - - - - - Maximum queue size. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Maximum queue size. - - - - - Maximum current connections. 0 = no maximum. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Indicates whether to keep connection open whenever possible. - - - - - NDLC item separator. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Renderer for log4j:event logger-xml-attribute (Default ${logger}) - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Option to include all properties from the log events - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Indicates whether to perform layout calculation. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether performance counter should be automatically created. - - - - - Name of the performance counter category. - - - - - Counter help text. - - - - - Name of the performance counter. - - - - - Performance counter type. - - - - - The value by which to increment the counter. - - - - - Performance counter instance name. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Default filter to be applied when no specific rule matches. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - Condition to be tested. - - - - - Resulting filter to be applied when the condition matches. - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of times to repeat each log message. - - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of retries that should be attempted on the wrapped target in case of a failure. - - - - - Time to wait between retries in milliseconds. - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Forward to (Instead of ) - - - - - Always use independent of - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. - - - - - Web service method name. Only used with Soap. - - - - - Web service namespace. Only used with Soap. - - - - - Protocol to be used when calling web service. - - - - - Custom proxy address, include port separated by a colon - - - - - Encoding. - - - - - Web service URL. - - - - - Value whether escaping be done according to the old NLog style (Very non-standard) - - - - - Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) - - - - - Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) - - - - - Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). - - - - - (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). - - - - - Proxy configuration when calling web service - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). - - - - - Column delimiter. - - - - - Quote Character. - - - - - Quoting mode. - - - - - Indicates whether CVS should include header. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout of the column. - - - - - Name of the column. - - - - - Override of Quoting mode - - - - - - - - - - - - - - - - - - - - - Should forward slashes be escaped? If true, / will be converted to \/ - - - - - Option to render the empty object value {} - - - - - Option to suppress the extra spaces in the output json - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as JSON) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the JSON serializer follow object references before backing off - - - - - - - - - - - - - - - - - Layout that will be rendered as the attribute's value. - - - - - Name of the attribute. - - - - - Determines whether or not this attribute will be Json encoded. - - - - - Should forward slashes be escaped? If true, / will be converted to \/ - - - - - Indicates whether to escape non-ascii characters - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - - - - - - - - - - - - - - - - - Option to include all properties from the log events - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - - - - - - - - - - Layout text. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as XML) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the XML serializer follow object references before backing off - - - - - XML element name to use for rendering IList-collections items - - - - - XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included - - - - - XML element name to use when rendering properties - - - - - XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value - - - - - Name of the root XML element - - - - - Value inside the root XML element - - - - - Whether a ElementValue with empty value should be included in the output - - - - - Auto indent and create new lines - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - - - - - - - - - - - Layout that will be rendered as the attribute's value. - - - - - Name of the attribute. - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - - - - - - - - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - Name of the element - - - - - Value inside the element - - - - - Whether a ElementValue with empty value should be included in the output - - - - - Auto indent and create new lines - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as XML) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the XML serializer follow object references before backing off - - - - - XML element name to use for rendering IList-collections items - - - - - XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included - - - - - XML element name to use when rendering properties - - - - - XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Condition expression. - - - - - - - - - - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Default number of unique filter values to expect, will automatically increase if needed - - - - - Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. - - - - - Layout to be used to filter log messages. - - - - - Max number of unique filter values to expect simultaneously - - - - - Max length of filter values, will truncate if above limit - - - - - How long before a filter expires, and logging is accepted again - - - - - Default buffer size for the internal buffers - - - - - Reuse internal buffers, and doesn't have to constantly allocate new buffers - - - - - Append FilterCount to the when an event is no longer filtered - - - - - Insert FilterCount value into when an event is no longer filtered - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs deleted file mode 100644 index fecc934b..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Mvc5Net48")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Mvc5Net48")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("91b2ac8d-1516-4d84-af08-d72a50854607")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs b/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs deleted file mode 100644 index 7cdb30a7..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Web; -using Microsoft.Extensions.DependencyInjection; - -namespace Mvc5Net48 -{ - internal class ServiceScopeHttpModule : IHttpModule - { - private static ServiceProvider s_serviceProvider; - - public static void SetServiceProvider(IServiceProvider serviceProvider) - { - s_serviceProvider = serviceProvider as ServiceProvider; - } - - public void Dispose() - { - s_serviceProvider?.Dispose(); - } - - public void Init(HttpApplication context) - { - context.BeginRequest += this.Context_BeginRequest; - context.EndRequest += this.Context_EndRequest; - } - - private void Context_BeginRequest(object sender, EventArgs e) - { - var context = ((HttpApplication) sender).Context; - context.Items[typeof(IServiceScope)] = s_serviceProvider.CreateScope(); - } - - private void Context_EndRequest(object sender, EventArgs e) - { - var context = ((HttpApplication) sender).Context; - if (context.Items[typeof(IServiceScope)] is IServiceScope scope) - { - scope.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml b/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml deleted file mode 100644 index 8ce20645..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml +++ /dev/null @@ -1,18 +0,0 @@ -@model dynamic - -@{ - Layout = null; -} - - - - - - title - - -
- @Html.Raw(HttpUtility.HtmlDecode(@ViewBag.Message)) -
- - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml b/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml deleted file mode 100644 index 8ce20645..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml +++ /dev/null @@ -1,18 +0,0 @@ -@model dynamic - -@{ - Layout = null; -} - - - - - - title - - -
- @Html.Raw(HttpUtility.HtmlDecode(@ViewBag.Message)) -
- - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/web.config b/DI/Lab.MsDI/Mvc5Net48/Views/web.config deleted file mode 100644 index 3a3275a4..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Views/web.config +++ /dev/null @@ -1,42 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config b/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config deleted file mode 100644 index fae9cfef..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.Release.config b/DI/Lab.MsDI/Mvc5Net48/Web.Release.config deleted file mode 100644 index da6e960b..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.config b/DI/Lab.MsDI/Mvc5Net48/Web.config deleted file mode 100644 index 58fb7220..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/Web.config +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDI/Mvc5Net48/packages.config b/DI/Lab.MsDI/Mvc5Net48/packages.config deleted file mode 100644 index 138971e0..00000000 --- a/DI/Lab.MsDI/Mvc5Net48/packages.config +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs deleted file mode 100644 index e9e64035..00000000 --- a/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Http.Dependencies; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public class DefaultDependencyResolver : IDependencyResolver - { - private readonly IServiceProvider _serviceProvider; - private IServiceScope _serviceScope; - - public DefaultDependencyResolver(IServiceProvider serviceProvider, IServiceScope serviceScope = null) - { - this._serviceProvider = serviceProvider; - this._serviceScope = serviceScope; - } - - public object GetService(Type serviceType) - { - return this._serviceProvider.GetService(serviceType); - } - - public IEnumerable GetServices(Type serviceType) - { - return this._serviceProvider.GetServices(serviceType); - } - - public IDependencyScope BeginScope() - { - this._serviceScope = this._serviceProvider.CreateScope(); - return new DefaultDependencyResolver(this._serviceScope.ServiceProvider,this._serviceScope); - } - - public void Dispose() - { - this._serviceScope?.Dispose(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs deleted file mode 100644 index fee3990d..00000000 --- a/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Linq; -using System.Web.Http; -using System.Web.Http.Controllers; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public class DependencyInjectionConfig - { - public static void Register(HttpConfiguration config) - { - var services = ConfigureServices(); - - var provider = services.BuildServiceProvider(); - - var resolver = new DefaultDependencyResolver(provider); - config.DependencyResolver = resolver; - } - - /// - /// 使用 MS DI 註冊 - /// - /// - private static ServiceCollection ConfigureServices() - { - var services = new ServiceCollection(); - - //使用 Microsoft.Extensions.DependencyInjection 註冊 - services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly.GetExportedTypes()); - - services.AddScoped(); - - services.AddTransient() - .AddSingleton() - .AddScoped(); - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs deleted file mode 100644 index ae5524bc..00000000 --- a/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Http.Controllers; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public static class ServiceProviderExtensions - { - public static IServiceCollection AddControllersAsServices(this IServiceCollection services, - IEnumerable controllerTypes) - { - var filter = controllerTypes.Where(t => !t.IsAbstract - && !t.IsGenericTypeDefinition) - .Where(t => typeof(IHttpController).IsAssignableFrom(t) - || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); - - foreach (var type in filter) - { - services.AddTransient(type); - } - - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs deleted file mode 100644 index 19e9c7a4..00000000 --- a/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Web.Http; - -namespace WebApiNet48 -{ - public static class WebApiConfig - { - public static void Register(HttpConfiguration config) - { - // Web API configuration and services - config.Filters.Add(new LogFilterAttribute()); - - // Web API routes - config.MapHttpAttributeRoutes(); - - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new { id = RouteParameter.Optional } - ); - } - } -} diff --git a/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs b/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs deleted file mode 100644 index ea631cce..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Net.Http; -using System.Web.Http; -using NLog; - -namespace WebApiNet48.Controllers -{ - public class Default1Controller : ApiController - { - [HttpGet] - public IHttpActionResult Get() - { - var logger = LogManager.GetCurrentClassLogger(); - var requestScope = this.Request.GetDependencyScope(); - var transient = requestScope.GetService(typeof(ITransientMessager)) as ITransientMessager; - var scope = requestScope.GetService(typeof(IScopeMessager)) as IScopeMessager; - var single = requestScope.GetService(typeof(ISingleMessager)) as ISingleMessager; - var content = "我在 Controller.Get Action\r\n" + - $"transient:{transient.OperationId}\r\n" + - $"scope:{scope.OperationId}\r\n" + - $"single:{single.OperationId}"; - Console.WriteLine(content); - logger.Info(content); - return this.Ok(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs deleted file mode 100644 index 51985f63..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Web.Http; -using NLog; - -namespace WebApiNet48.Controllers -{ - public class DefaultController : ApiController - { - private IMessager Transient { get; } - - private IMessager Scope { get; } - - private IMessager Single { get; } - - public DefaultController(ITransientMessager transient, - IScopeMessager scope, - ISingleMessager single) - { - this.Transient = transient; - this.Scope = scope; - this.Single = single; - } - - [HttpGet] - public IHttpActionResult Get() - { - var logger = LogManager.GetCurrentClassLogger(); - - var content = "我在 Controller.Get Action\r\n" + - $"transient:{this.Transient.OperationId}\r\n" + - $"scope:{this.Scope.OperationId}\r\n" + - $"single:{this.Single.OperationId}"; - Console.WriteLine(content); - logger.Info(content); - - //this._logger.LogInformation("transient = {transient},scope = {scope},single = {single}", - // this.Transient.OperationId, - // this.Scope.OperationId, - // this.Single.OperationId); - return this.Ok(content); - } - - - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Global.asax b/DI/Lab.MsDI/WebApiNet48/Global.asax deleted file mode 100644 index 593fb3aa..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="WebApiNet48.WebApiApplication" Language="C#" %> diff --git a/DI/Lab.MsDI/WebApiNet48/Global.asax.cs b/DI/Lab.MsDI/WebApiNet48/Global.asax.cs deleted file mode 100644 index 3cb9d613..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Global.asax.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Web; -using System.Web.Http; - -namespace WebApiNet48 -{ - public class WebApiApplication : HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - GlobalConfiguration.Configure(DependencyInjectionConfig.Register); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs b/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs deleted file mode 100644 index f2055f34..00000000 --- a/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Net.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Filters; -using NLog; - -namespace WebApiNet48 -{ - public class LogFilterAttribute : ActionFilterAttribute - { - public override void OnActionExecuting(HttpActionContext actionContext) - { - var requestScope = actionContext.Request.GetDependencyScope(); - - var transient = requestScope.GetService(typeof(ITransientMessager)) as MultiMessager; - var scope = requestScope.GetService(typeof(IScopeMessager)) as MultiMessager; - var single = requestScope.GetService(typeof(ISingleMessager)) as MultiMessager; - - var logger = LogManager.GetCurrentClassLogger(); - var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + - $"transient:{transient.OperationId}\r\n" + - $"scope:{scope.OperationId}\r\n" + - $"single:{single.OperationId}"; - Console.WriteLine(content); - logger.Info(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs deleted file mode 100644 index 9883346c..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNet48 -{ - public interface IMessager:IDisposable - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs deleted file mode 100644 index a29d7677..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNet48 -{ - public interface IScopeMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs deleted file mode 100644 index 27284507..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNet48 -{ - public interface ISingleMessager : IMessager - { - } -} diff --git a/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs deleted file mode 100644 index d9314497..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNet48 -{ - public interface ITransientMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs deleted file mode 100644 index a73f42f4..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace WebApiNet48 -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(LogMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs deleted file mode 100644 index 8aed0c19..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace WebApiNet48 -{ - internal class MachineMessager : IMessager - { - public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(MachineMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs deleted file mode 100644 index 7e3edf7b..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace WebApiNet48 -{ - public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager - { - public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(MultiMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/NLog.config b/DI/Lab.MsDI/WebApiNet48/NLog.config deleted file mode 100644 index df272249..00000000 --- a/DI/Lab.MsDI/WebApiNet48/NLog.config +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDI/WebApiNet48/NLog.xsd b/DI/Lab.MsDI/WebApiNet48/NLog.xsd deleted file mode 100644 index 32246a71..00000000 --- a/DI/Lab.MsDI/WebApiNet48/NLog.xsd +++ /dev/null @@ -1,3644 +0,0 @@ - - - - - - - - - - - - - - - Watch config file for changes and reload automatically. - - - - - Print internal NLog messages to the console. Default value is: false - - - - - Print internal NLog messages to the console error output. Default value is: false - - - - - Write internal NLog messages to the specified file. - - - - - Log level threshold for internal log messages. Default value is: Info. - - - - - Global log level threshold for application log messages. Messages below this level won't be logged. - - - - - Throw an exception when there is an internal error. Default value is: false. Not recommend to set to true in production! - - - - - Throw an exception when there is a configuration error. If not set, determined by throwExceptions. - - - - - Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. - - - - - Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. - - - - - Write timestamps for internal NLog messages. Default value is: true. - - - - - Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. - - - - - Perform message template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. - - - - - - - - - - - - - - Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). - - - - - - - - - - - - - - - - - Prefix for targets/layout renderers/filters/conditions loaded from this assembly. - - - - - Load NLog extensions from the specified file (*.dll) - - - - - Load NLog extensions from the specified assembly. Assembly name should be fully qualified. - - - - - - - - - - Filter on the name of the logger. May include wildcard characters ('*' or '?'). - - - - - Comma separated list of levels that this rule matches. - - - - - Minimum level that this rule matches. - - - - - Maximum level that this rule matches. - - - - - Level that this rule matches. - - - - - Comma separated list of target names. - - - - - Ignore further rules if this one matches. - - - - - Enable this rule. Note: disabled rules aren't available from the API. - - - - - Rule identifier to allow rule lookup with Configuration.FindRuleByName and Configuration.RemoveRuleByName. - - - - - - - - - - - - - - - Default action if none of the filters match. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. - - - - - Ignore any errors in the include file. - - - - - - - - Variable value. Note, the 'value' attribute has precedence over this one. - - - - - - Variable name. - - - - - Variable value. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events that should be processed in a batch by the lazy writer thread. - - - - - Whether to use the locking queue, instead of a lock-free concurrent queue The locking queue is less concurrent when many logger threads, but reduces memory allocation - - - - - Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch - - - - - Action to be taken when the lazy writer thread request queue count exceeds the set limit. - - - - - Limit on the number of requests in the lazy writer thread request queue. - - - - - Time in milliseconds to sleep between batches. (1 or less means trigger on new activity) - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - Delay the flush until the LogEvent has been confirmed as written - - - - - Condition expression. Log events who meet this condition will cause a flush on the wrapped target. - - - - - Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Number of log events to be buffered. - - - - - Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. - - - - - Action to take if the buffer overflows. - - - - - Indicates whether to use sliding timeout. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Maximum queue size. - - - - - Maximum current connections. 0 = no maximum. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Indicates whether to keep connection open whenever possible. - - - - - NDLC item separator. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Renderer for log4j:event logger-xml-attribute (Default ${logger}) - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Option to include all properties from the log events - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Viewer parameter name. - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - Enables output using ANSI Color Codes - - - - - The encoding for writing messages to the . - - - - - Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). - - - - - Indicates whether to auto-flush after - - - - - Indicates whether to auto-check if the console has been redirected to file - Disables coloring logic when System.Console.IsOutputRedirected = true - - - - - Indicates whether to use default row highlighting rules. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Condition that must be met in order to set the specified foreground and background color. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - - Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. - - - - - Condition that must be met before scanning the row for highlight of words - - - - - Indicates whether to ignore case when comparing texts. - - - - - Regular expression to be matched. You must specify either text or regex. - - - - - Text to be matched. You must specify either text or regex. - - - - - Indicates whether to match whole words only. - - - - - Background color. - - - - - Foreground color. - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether to auto-flush after - - - - - Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) - - - - - The encoding for writing messages to the . - - - - - Indicates whether to send the log messages to the standard error instead of the standard output. - - - - - Whether to enable batch writing using char[]-buffers, instead of using - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. - - - - - Indicates whether to keep the database connection open between the log events. - - - - - Name of the database provider. - - - - - Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. - - - - - Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. - - - - - Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. - - - - - Name of the connection string (as specified in <connectionStrings> configuration section. - - - - - Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. - - - - - Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. - - - - - Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. - - - - - Configures isolated transaction batch writing. If supported by the database, then it will improve insert performance. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Text of the SQL command to be run on each log level. - - - - - Type of the SQL command to be run on each log level. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Convert format of the property value - - - - - Culture used for parsing property string-value for type-conversion - - - - - Value to assign on the object-property - - - - - Name for the object-property - - - - - Type of the object-property - - - - - - - - - - - - - - Type of the command. - - - - - Connection string to run the command against. If not provided, connection string from the target is used. - - - - - Indicates whether to ignore failures. - - - - - Command text. - - - - - - - - - - - - - - - - - - - Database parameter name. - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Database parameter DbType. - - - - - Database parameter size. - - - - - Database parameter precision. - - - - - Database parameter scale. - - - - - Type of the parameter. - - - - - Whether empty value should translate into DbNull. Requires database column to allow NULL values. - - - - - Convert format of the database parameter value. - - - - - Culture used for parsing parameter string-value for type-conversion - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Layout that renders event Category. - - - - - Optional entry type. When not set, or when not convertible to then determined by - - - - - Layout that renders event ID. - - - - - Name of the Event Log to write to. This can be System, Application or any user-defined name. - - - - - Name of the machine on which Event Log service is running. - - - - - Maximum Event log size in kilobytes. - - - - - Message length limit to write to the Event Log. - - - - - Value to be used as the event Source. - - - - - Action to take if the message is larger than the option. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether to return to the first target after any successful write. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - File encoding. - - - - - Line ending mode. - - - - - Maximum days of archive files that should be kept. - - - - - Indicates whether to compress archive files into the zip archive format. - - - - - Way file archives are numbered. - - - - - Name of the file to be used for an archive. - - - - - Is the an absolute or relative path? - - - - - Indicates whether to automatically archive log files every time the specified time passes. - - - - - Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: - - - - - Maximum number of archive files that should be kept. - - - - - Indicates whether the footer should be written only when the file is archived. - - - - - Maximum number of log file names that should be stored as existing. - - - - - Indicates whether to delete old log file on startup. - - - - - File attributes (Windows only). - - - - - Indicates whether to create directories if they do not exist. - - - - - Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. - - - - - Value of the file size threshold to archive old log file on startup. - - - - - Indicates whether to archive old log file on startup. - - - - - Value specifying the date format to use when archiving files. - - - - - Indicates whether to enable log file(s) to be deleted. - - - - - Indicates whether to write BOM (byte order mark) in created files - - - - - Indicates whether to replace file contents on each write instead of appending log message at the end. - - - - - Indicates whether file creation calls should be synchronized by a system global mutex. - - - - - Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. - - - - - Is the an absolute or relative path? - - - - - Name of the file to write to. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Indicates whether concurrent writes to the log file by multiple processes on different network hosts. - - - - - Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. - - - - - Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). - - - - - Indicates whether to keep log file open instead of opening and closing it on each logging event. - - - - - Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write - - - - - Indicates whether concurrent writes to the log file by multiple processes on the same host. - - - - - Number of times the write is appended on the file before NLog discards the log message. - - - - - Delay in milliseconds to wait before attempting to write to the file again. - - - - - Log file buffer size in bytes. - - - - - Maximum number of seconds before open files are flushed. If this number is negative or zero the files are not flushed by timer. - - - - - Indicates whether to automatically flush the file buffers after each log message. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Condition expression. Log events who meet this condition will be forwarded to the wrapped target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Windows domain name to change context to. - - - - - Required impersonation level. - - - - - Type of the logon provider. - - - - - Logon Type. - - - - - User account password. - - - - - Indicates whether to revert to the credentials of the process instead of impersonating another user. - - - - - Username to change context to. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Interval in which messages will be written up to the number of messages. - - - - - Maximum allowed number of messages written per . - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Endpoint address. - - - - - Name of the endpoint configuration in WCF configuration file. - - - - - Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) - - - - - Client ID. - - - - - Indicates whether to include per-event properties in the payload sent to the server. - - - - - Indicates whether to use binary message encoding. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Layout that should be use to calculate the value for the parameter. - - - - - Name of the parameter. - - - - - Type of the parameter. - - - - - Type of the parameter. Obsolete alias for - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Text to be rendered. - - - - - Header. - - - - - Footer. - - - - - Indicates whether NewLine characters in the body should be replaced with tags. - - - - - Priority used for sending mails. - - - - - Encoding to be used for sending e-mail. - - - - - BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Indicates whether to add new lines between log entries. - - - - - Indicates whether to send message as HTML instead of plain text. - - - - - Sender's email address (e.g. joe@domain.com). - - - - - Mail message body (repeated for each log message send in one mail). - - - - - Mail subject. - - - - - Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Indicates the SMTP client timeout. - - - - - SMTP Server to be used for sending. - - - - - SMTP Authentication mode. - - - - - Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). - - - - - Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. - - - - - Port number that SMTP Server is listening on. - - - - - Indicates whether the default Settings from System.Net.MailSettings should be used. - - - - - Folder where applications save mail messages to be processed by the local SMTP server. - - - - - Specifies how outgoing email messages will be handled. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Max number of items to have in memory - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Class name. - - - - - Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Encoding to be used. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Indicates whether to keep connection open whenever possible. - - - - - Maximum current connections. 0 = no maximum. - - - - - Maximum queue size. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Encoding to be used. - - - - - Instance of that is used to format log messages. - - - - - End of line value if a newline is appended at the end of log message . - - - - - Maximum message size in bytes. - - - - - Indicates whether to append newline at the end of log message. - - - - - Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. - - - - - Network address. - - - - - Size of the connection cache (number of connections which are kept alive). - - - - - The number of seconds a connection will remain idle before the first keep-alive probe is sent - - - - - Maximum queue size. - - - - - Maximum current connections. 0 = no maximum. - - - - - Action that should be taken if the will be more connections than . - - - - - Action that should be taken if the message is larger than maxMessageSize. - - - - - Indicates whether to keep connection open whenever possible. - - - - - NDLC item separator. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - Renderer for log4j:event logger-xml-attribute (Default ${logger}) - - - - - Indicates whether to include NLog-specific extensions to log4j schema. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include stack contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include dictionary contents. - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Option to include all properties from the log events - - - - - AppInfo field. By default it's the friendly name of the current AppDomain. - - - - - NDC item separator. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Indicates whether to perform layout calculation. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Indicates whether performance counter should be automatically created. - - - - - Name of the performance counter category. - - - - - Counter help text. - - - - - Name of the performance counter. - - - - - Performance counter type. - - - - - The value by which to increment the counter. - - - - - Performance counter instance name. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Default filter to be applied when no specific rule matches. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - Condition to be tested. - - - - - Resulting filter to be applied when the condition matches. - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of times to repeat each log message. - - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Number of retries that should be attempted on the wrapped target in case of a failure. - - - - - Time to wait between retries in milliseconds. - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Layout used to format log messages. - - - - - Forward to (Instead of ) - - - - - Always use independent of - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name of the target. - - - - - Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit - - - - - Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. - - - - - Web service method name. Only used with Soap. - - - - - Web service namespace. Only used with Soap. - - - - - Protocol to be used when calling web service. - - - - - Custom proxy address, include port separated by a colon - - - - - Encoding. - - - - - Web service URL. - - - - - Value whether escaping be done according to the old NLog style (Very non-standard) - - - - - Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) - - - - - Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) - - - - - Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). - - - - - (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). - - - - - Proxy configuration when calling web service - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). - - - - - Column delimiter. - - - - - Quote Character. - - - - - Quoting mode. - - - - - Indicates whether CVS should include header. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Layout of the column. - - - - - Name of the column. - - - - - Override of Quoting mode - - - - - - - - - - - - - - - - - - - - - Should forward slashes be escaped? If true, / will be converted to \/ - - - - - Option to render the empty object value {} - - - - - Option to suppress the extra spaces in the output json - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as JSON) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the JSON serializer follow object references before backing off - - - - - - - - - - - - - - - - - Layout that will be rendered as the attribute's value. - - - - - Name of the attribute. - - - - - Determines whether or not this attribute will be Json encoded. - - - - - Should forward slashes be escaped? If true, / will be converted to \/ - - - - - Indicates whether to escape non-ascii characters - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - Footer layout. - - - - - Header layout. - - - - - Body layout (can be repeated multiple times). - - - - - - - - - - - - - - - - - - - - - Option to include all properties from the log events - - - - - Indicates whether to include call site (class and method name) in the information sent over the network. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include contents of the stack. - - - - - Indicates whether to include source info (file name and line number) in the information sent over the network. - - - - - - - - - - - - - - Layout text. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as XML) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the XML serializer follow object references before backing off - - - - - XML element name to use for rendering IList-collections items - - - - - XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included - - - - - XML element name to use when rendering properties - - - - - XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value - - - - - Name of the root XML element - - - - - Value inside the root XML element - - - - - Whether a ElementValue with empty value should be included in the output - - - - - Auto indent and create new lines - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - - - - - - - - - - - Layout that will be rendered as the attribute's value. - - - - - Name of the attribute. - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - Whether an attribute with empty value should be included in the output - - - - - - - - - - - - - - - - - - - - - - - - - Determines whether or not this attribute will be Xml encoded. - - - - - Name of the element - - - - - Value inside the element - - - - - Whether a ElementValue with empty value should be included in the output - - - - - Auto indent and create new lines - - - - - List of property names to exclude when is true - - - - - Option to include all properties from the log event (as XML) - - - - - Indicates whether to include contents of the dictionary. - - - - - Indicates whether to include contents of the dictionary. - - - - - How far should the XML serializer follow object references before backing off - - - - - XML element name to use for rendering IList-collections items - - - - - XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included - - - - - XML element name to use when rendering properties - - - - - XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Condition expression. - - - - - - - - - - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - Substring to be matched. - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - String to compare the layout to. - - - - - Indicates whether to ignore case when comparing strings. - - - - - Layout to be used to filter log messages. - - - - - - - - - - - - - - - - - - - - - - - - Action to be taken when filter matches. - - - - - Default number of unique filter values to expect, will automatically increase if needed - - - - - Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. - - - - - Layout to be used to filter log messages. - - - - - Max number of unique filter values to expect simultaneously - - - - - Max length of filter values, will truncate if above limit - - - - - How long before a filter expires, and logging is accepted again - - - - - Default buffer size for the internal buffers - - - - - Reuse internal buffers, and doesn't have to constantly allocate new buffers - - - - - Append FilterCount to the when an event is no longer filtered - - - - - Insert FilterCount value into when an event is no longer filtered - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs deleted file mode 100644 index f5b17259..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("WebApiNet48")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WebApiNet48")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("429d865c-ede7-4ea1-bc51-9a5bcab26bb8")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs b/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs deleted file mode 100644 index ccb1df5c..00000000 --- a/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Http.Dependencies; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - internal class ServiceProviderDependencyResolver : IDependencyResolver - { - protected IServiceProvider ServiceProvider { get; set; } - - public ServiceProviderDependencyResolver(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - public object GetService(Type serviceType) - { - if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) - { - return scope.ServiceProvider.GetService(serviceType); - } - - return this.ServiceProvider.GetService(serviceType); - throw new InvalidOperationException("IServiceScope not provided"); - } - - public IEnumerable GetServices(Type serviceType) - { - if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) - { - return scope.ServiceProvider.GetServices(serviceType); - } - - return this.ServiceProvider.GetServices(serviceType); - throw new InvalidOperationException("IServiceScope not provided"); - } - - public IDependencyScope BeginScope() - { - return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); - } - - public void Dispose() - { - //throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs b/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs deleted file mode 100644 index d326414f..00000000 --- a/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Web; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - internal class ServiceScopeModule : IHttpModule - { - private static ServiceProvider _serviceProvider; - - public void Dispose() - { - - } - - public void Init(HttpApplication context) - { - context.BeginRequest += this.Context_BeginRequest; - context.EndRequest += this.Context_EndRequest; - } - - private void Context_EndRequest(object sender, EventArgs e) - { - var context = ((HttpApplication)sender).Context; - if (context.Items[typeof(IServiceScope)] is IServiceScope scope) - { - scope.Dispose(); - } - } - - private void Context_BeginRequest(object sender, EventArgs e) - { - var context = ((HttpApplication)sender).Context; - context.Items[typeof(IServiceScope)] = _serviceProvider.CreateScope(); - } - - public static void SetServiceProvider(ServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.Debug.config b/DI/Lab.MsDI/WebApiNet48/Web.Debug.config deleted file mode 100644 index fae9cfef..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.Release.config b/DI/Lab.MsDI/WebApiNet48/Web.Release.config deleted file mode 100644 index da6e960b..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.config b/DI/Lab.MsDI/WebApiNet48/Web.config deleted file mode 100644 index 491d448d..00000000 --- a/DI/Lab.MsDI/WebApiNet48/Web.config +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj deleted file mode 100644 index 32461adc..00000000 --- a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj +++ /dev/null @@ -1,186 +0,0 @@ - - - - - Debug - AnyCPU - - - 2.0 - {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - WebApiNet48 - WebApiNet48 - v4.8 - true - - 44304 - - - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - true - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - - - ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - ..\packages\NLog.4.7.5\lib\net45\NLog.dll - - - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - - - - - - - - - - - - - - - - - - - - ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll - - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - - - - - - - - - - - - - - Global.asax - - - - - - - - - - - - - - - - PreserveNewest - - - Designer - - - - Web.config - - - Web.config - - - - - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 50548 - / - https://localhost:44304/ - False - False - - - False - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings deleted file mode 100644 index 8a5228f0..00000000 --- a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/packages.config b/DI/Lab.MsDI/WebApiNet48/packages.config deleted file mode 100644 index e8fcf0e2..00000000 --- a/DI/Lab.MsDI/WebApiNet48/packages.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs b/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs deleted file mode 100644 index 38dc48ee..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace WebApiNetCore31.Controllers -{ - [ApiController] - [Route("[controller]")] - public class Default1Controller : ControllerBase - { - private readonly ILogger _logger; - - public Default1Controller(ILogger logger) - { - this._logger = logger; - } - - [HttpGet] - public IActionResult Get() - { - var serviceProvider = this.HttpContext.RequestServices; - var transient = serviceProvider.GetService(typeof(ITransientMessager)) as ITransientMessager; - var scope = serviceProvider.GetService(typeof(IScopeMessager)) as IScopeMessager; - var single = serviceProvider.GetService(typeof(ISingleMessager)) as ISingleMessager; - var content = $"transient:{transient.OperationId}\r\n" + - $"scope:{scope.OperationId}\r\n" + - $"single:{single.OperationId}"; - - this._logger.LogInformation("transient = {transient},scope = {scope},single = {single}", - transient.OperationId, - scope.OperationId, - single.OperationId); - return this.Ok(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs deleted file mode 100644 index 2fe6ac31..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace WebApiNetCore31.Controllers -{ - [ApiController] - [Route("[controller]")] - public class DefaultController : ControllerBase - { - private IMessager Transient { get; } - - private IMessager Scope { get; } - - private IMessager Single { get; } - - private readonly ILogger _logger; - - public DefaultController(ILogger logger, - ITransientMessager transient, - IScopeMessager scope, - ISingleMessager single) - { - this._logger = logger; - - this.Transient = transient; - this.Scope = scope; - this.Single = single; - } - - [HttpGet] - public IActionResult Get() - { - var content = "我在 DefaultController.Get \r\n" + - $"transient:{this.Transient.OperationId}\r\n" + - $"scope:{this.Scope.OperationId}\r\n" + - $"single:{this.Single.OperationId}"; - this._logger.LogInformation("我在 DefaultController.Get ,transient = {transient},scope = {scope},single = {single}", - this.Transient.OperationId, - this.Scope.OperationId, - this.Single.OperationId); - return this.Ok(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs b/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs deleted file mode 100644 index 72b0c840..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace WebApiNetCore31 -{ - public class LogFilterAttribute : ActionFilterAttribute - { - public LogFilterAttribute() - { - - } - public override void OnActionExecuting(ActionExecutingContext context) - { - var transient = context.HttpContext.RequestServices.GetService(); - var scope = context.HttpContext.RequestServices.GetService(); - var single = context.HttpContext.RequestServices.GetService(); - var logger = context.HttpContext.RequestServices.GetService>(); - logger.LogInformation("我在 LogFilterAttribute ,transient = {transient},scope = {scope},single = {single}", - transient.OperationId, - scope.OperationId, - single.OperationId); - - //Console.WriteLine(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs deleted file mode 100644 index 479144a9..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - public interface IMessager:IDisposable - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs deleted file mode 100644 index 101c08fa..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface IScopeMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs deleted file mode 100644 index e803b8c9..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface ISingleMessager : IMessager - { - } -} diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs deleted file mode 100644 index b152a276..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface ITransientMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs deleted file mode 100644 index 9819ac66..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(LogMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs deleted file mode 100644 index ed49b5dd..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - internal class MachineMessager : IMessager - { - public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; - public void Dispose() - { - Console.WriteLine($"{nameof(MachineMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs deleted file mode 100644 index 68182ed0..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager - { - public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; - - public void Dispose() - { - Console.WriteLine($"{nameof(MultiMessager)} GC"); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Program.cs b/DI/Lab.MsDI/WebApiNetCore31/Program.cs deleted file mode 100644 index aba702a0..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace WebApiNetCore31 -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} diff --git a/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json b/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json deleted file mode 100644 index e27ff49d..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55458", - "sslPort": 44311 - } - }, - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "default", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "WebApiNetCore31": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "weatherforecast", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Startup.cs b/DI/Lab.MsDI/WebApiNetCore31/Startup.cs deleted file mode 100644 index 60390753..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Startup.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace WebApiNetCore31 -{ - public class Startup - { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - //services.AddControllers(); - services.AddControllers(options => options.Filters.Add(new LogFilterAttribute())); - - services.AddTransient(p => new Worker(new LogMessager())); - services.AddTransient(p => new Worker2(new MachineMessager())); - - services.AddTransient() - .AddSingleton() - .AddScoped() - ; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs b/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs deleted file mode 100644 index 1bb9e27c..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} diff --git a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj deleted file mode 100644 index d12c450b..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - netcoreapp3.1 - - - - diff --git a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings deleted file mode 100644 index 8a5228f0..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Worker.cs b/DI/Lab.MsDI/WebApiNetCore31/Worker.cs deleted file mode 100644 index f069f7e4..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/Worker.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace WebApiNetCore31 -{ - public class Worker - { - public IMessager Messager { get; set; } - - public Worker(IMessager messager) - { - this.Messager = messager; - } - } - - public class Worker2 - { - public IMessager Messager { get; set; } - - public Worker2(IMessager messager) - { - this.Messager = messager; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json b/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json deleted file mode 100644 index 8983e0fc..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/DI/Lab.MsDI/WebApiNetCore31/appsettings.json b/DI/Lab.MsDI/WebApiNetCore31/appsettings.json deleted file mode 100644 index d9d9a9bf..00000000 --- a/DI/Lab.MsDI/WebApiNetCore31/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App.config b/DI/Lab.MsDI/WebApiOwinNet48/App.config deleted file mode 100644 index 4a79e878..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/App.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs b/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs deleted file mode 100644 index cf2c1518..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace WebApiOwinNet48 -{ - public class ServerSetting - { - public const string HostEndpoint = "http://localhost:8001"; - - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs deleted file mode 100644 index d4d14983..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Http.Dependencies; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiOwinNet48 -{ - public class DefaultDependencyResolver : IDependencyResolver - { - protected IServiceProvider ServiceProvider { get; set; } - - public DefaultDependencyResolver(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - public object GetService(Type serviceType) - { - return this.ServiceProvider.GetService(serviceType); - } - - public IEnumerable GetServices(Type serviceType) - { - return this.ServiceProvider.GetServices(serviceType); - } - - public IDependencyScope BeginScope() - { - return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); - } - - public void Dispose() - { - // you can implement this interface just when you use .net core 2.0 - // this.ServiceProvider.Dispose(); - ((ServiceProvider) this.ServiceProvider).Dispose(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs deleted file mode 100644 index 0bb16012..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Web.Http; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiOwinNet48 -{ - public class DependencyInjectionConfig - { - public static void Register(HttpConfiguration config) - { - var services = ConfigureServices(); - - var provider = services.BuildServiceProvider(); - - var resolver = new DefaultDependencyResolver(provider); - config.DependencyResolver = resolver; - } - - /// - /// 使用 MS DI 註冊 - /// - /// - private static ServiceCollection ConfigureServices() - { - var services = new ServiceCollection(); - - //使用 Microsoft.Extensions.DependencyInjection 註冊 - services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly.GetExportedTypes()); - - services.AddScoped(); - - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs deleted file mode 100644 index ffb8667b..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Http.Controllers; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiOwinNet48 -{ - public static class ServiceProviderExtensions - { - public static IServiceCollection AddControllersAsServices(this IServiceCollection services, - IEnumerable controllerTypes) - { - var filter = controllerTypes.Where(t => !t.IsAbstract - && !t.IsGenericTypeDefinition) - .Where(t => typeof(IHttpController).IsAssignableFrom(t) - || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); - - foreach (var type in filter) - { - services.AddTransient(type); - } - - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs deleted file mode 100644 index 3a04e4e2..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Web.Http; - -namespace WebApiOwinNet48 -{ - public static class WebApiConfig - { - public static void Register(HttpConfiguration config) - { - // Web API configuration and services - // config.Filters.Add(new LogFilterAttribute()); - - // Web API routes - config.MapHttpAttributeRoutes(); - - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new { id = RouteParameter.Optional } - ); - - } - } -} diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs b/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs deleted file mode 100644 index 7a1b5afa..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace WebApiOwinNet48 -{ - public class Commander - { - public Guid Id { get; set; } = Guid.NewGuid(); - public string Get() - { - var msg = "GG"; - Console.WriteLine(msg); - return msg; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs deleted file mode 100644 index 99e3277a..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Web.Http; - -namespace WebApiOwinNet48.Controllers -{ - public class DefaultController : ApiController - { - private Commander _cmder; - - public DefaultController(Commander cmder) - { - this._cmder = cmder; - } - - // GET - public async Task Get() - { - // this._cmder.Get(); - Console.WriteLine($"我在 DefaultController.Get ,Command.Id = {this._cmder.Id}"); - return this.Ok(this._cmder); - } - - private class Member - { - public Guid Id { get; set; } - - public int Age { get; set; } - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs b/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs deleted file mode 100644 index 3811e5f3..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading.Tasks; -using Microsoft.Owin; -using AppFunc = System.Func, System.Threading.Tasks.Task>; - -namespace WebApiOwinNet48 -{ - public class LabMiddleware1 : OwinMiddleware - { - private readonly Commander _cmder; - - public LabMiddleware1(OwinMiddleware next, Commander cmder) : base(next) - { - this._cmder = this._cmder; - } - - // public LabMiddleware(OwinMiddleware next) : base(next) - // { - // } - - public override Task Invoke(IOwinContext context) - { - Console.WriteLine("我在 LabMiddleware.Invoke"); - - // Console.WriteLine($"我在 LabMiddleware.Invoke ,Command.Id = {this._cmder.Id}"); - return this.Next.Invoke(context); - } - } - - - public class LabMiddleware - { - private readonly AppFunc _next; - - public LabMiddleware(AppFunc next,Commander cmder) - { - if (next == null) - { - throw new ArgumentNullException("next"); - } - - this._next = next; - } - - public async Task Invoke(IDictionary environment) - { - try - { - await this._next(environment); - } - catch (Exception ex) - { - var owinContext = new OwinContext(environment); - - this.ErrorHandle(ex, owinContext); - } - } - - private void ErrorHandle(Exception ex, IOwinContext context) - { - context.Response.StatusCode = (int) HttpStatusCode.InternalServerError; - context.Response.ReasonPhrase = "Internal Server Error"; - context.Response.ContentType = "application/json"; - context.Response.Write(ex.Message); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Program.cs b/DI/Lab.MsDI/WebApiOwinNet48/Program.cs deleted file mode 100644 index c2b4659a..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/Program.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Microsoft.Owin.Hosting; - -namespace WebApiOwinNet48 -{ - internal class Program - { - private static void Main(string[] args) - { - using (WebApp.Start(ServerSetting.HostEndpoint)) - { - Console.WriteLine($"伺服器已啟動, 位置:{ServerSetting.HostEndpoint}"); - Console.WriteLine("按下任意建離開應用程式"); - Console.ReadLine(); - } - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs deleted file mode 100644 index 8a1889ce..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("WebApiOwinNet48")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WebApiOwinNet48")] -[assembly: AssemblyCopyright("Copyright © 2021")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("680ec3cf-cfe2-48f9-8006-f6a3d9b5dc42")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs b/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs deleted file mode 100644 index b76ef16c..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Web.Http; -using Owin; - -namespace WebApiOwinNet48 -{ - public class Startup - { - public void Configuration(IAppBuilder app) - { - var httpConfig = new HttpConfiguration(); - DependencyInjectionConfig.Register(httpConfig); - WebApiConfig.Register(httpConfig); - //app.UseErrorPage(); - //app.UseWelcomePage("/Welcome"); - // app.Use(); - app.UseWebApi(httpConfig); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj b/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj deleted file mode 100644 index dbd189c3..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj +++ /dev/null @@ -1,111 +0,0 @@ - - - - - Debug - AnyCPU - {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42} - Exe - WebApiOwinNet48 - WebApiOwinNet48 - v4.8 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - True - - - ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll - True - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - True - - - ..\packages\Microsoft.Owin.4.1.1\lib\net45\Microsoft.Owin.dll - - - ..\packages\Microsoft.Owin.Diagnostics.4.1.1\lib\net45\Microsoft.Owin.Diagnostics.dll - - - ..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll - - - ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll - - - - ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Owin.1.0\lib\net40\Owin.dll - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - True - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.7\lib\net45\System.Web.Http.Owin.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/packages.config b/DI/Lab.MsDI/WebApiOwinNet48/packages.config deleted file mode 100644 index 4d964e03..00000000 --- a/DI/Lab.MsDI/WebApiOwinNet48/packages.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/App.config b/DI/Lab.MsDI/WinFormNet48/App.config deleted file mode 100644 index 1a57e048..00000000 --- a/DI/Lab.MsDI/WinFormNet48/App.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs deleted file mode 100644 index 00209ab0..00000000 --- a/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; - -namespace WinFormNet48 -{ - internal class DependencyInjectionConfig - { - public static IServiceProvider ServiceProvider { get; set; } - - public static IServiceProvider Register() - { - var services = new ServiceCollection(); - ConfigureServices(services); - ServiceProvider = services.BuildServiceProvider(); - return ServiceProvider; - } - - /// - /// 使用 MS DI 註冊 - /// - private static IServiceCollection ConfigureServices(IServiceCollection services) - { - return services.AddSingleton() - .AddTransient() - .AddTransient() - .AddSingleton() - .AddScoped() - - //.AddTransient(provider => - // { - // var operation = provider.GetRequiredService(); - // return new Worker(operation); - // }) - //.AddTransient(provider => - // { - // var operation = provider.GetRequiredService(); - // return new Workflow(operation); - // }) - - //.AddTransient() - //.AddTransient() - //.AddLogging(loggingBuilder => - // { - // // configure Logging with NLog - // loggingBuilder.ClearProviders(); - // loggingBuilder.SetMinimumLevel(LogLevel.Trace); - // loggingBuilder.AddNLog(config); - // }) - ; - - ; - } - - //private static IConfiguration CreateConfig() - //{ - // var config = new ConfigurationBuilder() - // .SetBasePath(System.IO.Directory - // .GetCurrentDirectory()) //From NuGet Package Microsoft.Extensions.Configuration.Json - // .AddJsonFile("appsettings.json", true, true) - // .Build(); - // return config; - //} - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs deleted file mode 100644 index abde6f86..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace WinFormNet48 -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.button1 = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // button1 - // - this.button1.Location = new System.Drawing.Point(47, 64); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(75, 23); - this.button1.TabIndex = 0; - this.button1.Text = "button1"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.button1_Click); - // - // Form1 - // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Controls.Add(this.button1); - this.Name = "Form1"; - this.Text = "Form1"; - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.Button button1; - } -} - diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.cs b/DI/Lab.MsDI/WinFormNet48/Form1.cs deleted file mode 100644 index 3c3e1406..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Form1.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Windows.Forms; -using Microsoft.Extensions.DependencyInjection; - -namespace WinFormNet48 -{ - public partial class Form1 : Form - { - public Form1() - { - this.InitializeComponent(); - } - - private void button1_Click(object sender, EventArgs e) - { - var serviceProvider = DependencyInjectionConfig.ServiceProvider; - var work = serviceProvider.GetRequiredService(); - Console.WriteLine(work.Get()); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.resx b/DI/Lab.MsDI/WinFormNet48/Form1.resx deleted file mode 100644 index 1af7de15..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Form1.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs deleted file mode 100644 index 741af8b8..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace WinFormNet48 -{ - public interface IMessager - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs deleted file mode 100644 index 0241069d..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WinFormNet48 -{ - public interface IScopeMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs deleted file mode 100644 index 76e8fce5..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WinFormNet48 -{ - public interface ISingleMessager : IMessager - { - } -} diff --git a/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs deleted file mode 100644 index 0c0d795b..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WinFormNet48 -{ - public interface ITransientMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs deleted file mode 100644 index 7e18917a..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WinFormNet48 -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs deleted file mode 100644 index 2d0c9de3..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WinFormNet48 -{ - internal class MachineMessager : IMessager - { - public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs deleted file mode 100644 index 878b27bf..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WinFormNet48 -{ - public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager - { - public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Program.cs b/DI/Lab.MsDI/WinFormNet48/Program.cs deleted file mode 100644 index 9f3dda6e..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Windows.Forms; -using Microsoft.Extensions.DependencyInjection; - -namespace WinFormNet48 -{ - internal static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - private static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - var serviceProvider = DependencyInjectionConfig.Register() as ServiceProvider; - - using (serviceProvider) - { - var form = serviceProvider.GetService(typeof(Form1)) as Form; - Application.Run(form); - } - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs deleted file mode 100644 index b7aebc8e..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("WinFormNet48")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WinFormNet48")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("fc5aa11a-0879-43d8-8e79-dfc37de75fb8")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs deleted file mode 100644 index ca98cf3f..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WinFormNet48.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinFormNet48.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx deleted file mode 100644 index af7dbebb..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs deleted file mode 100644 index 8411c43f..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WinFormNet48.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings deleted file mode 100644 index 39645652..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj deleted file mode 100644 index 34b52830..00000000 --- a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj +++ /dev/null @@ -1,113 +0,0 @@ - - - - - Debug - AnyCPU - {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8} - Exe - WinFormNet48 - WinFormNet48 - v4.8 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - - - - - - Form1.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings deleted file mode 100644 index 8a5228f0..00000000 --- a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Worker.cs b/DI/Lab.MsDI/WinFormNet48/Worker.cs deleted file mode 100644 index d6bcf3f2..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Worker.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace WinFormNet48 -{ - public class Worker - { - private IMessager Transient { get; } - - private IMessager Scope { get; } - - private IMessager Single { get; } - - public Worker(ITransientMessager transient, IScopeMessager scope, ISingleMessager single) - { - this.Transient = transient; - this.Scope = scope; - this.Single = single; - } - - public string Get() - { - return $"transient:{this.Transient.OperationId}\r\n" + - $"scope:{this.Scope.OperationId}\r\n" + - $"single:{this.Single.OperationId}"; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Workflow.cs b/DI/Lab.MsDI/WinFormNet48/Workflow.cs deleted file mode 100644 index 8298237c..00000000 --- a/DI/Lab.MsDI/WinFormNet48/Workflow.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WinFormNet48 -{ - public class Workflow - { - public Workflow(IMessager operation) - { - Console.WriteLine(operation.OperationId); - } - } -} diff --git a/DI/Lab.MsDI/WinFormNet48/packages.config b/DI/Lab.MsDI/WinFormNet48/packages.config deleted file mode 100644 index a2075034..00000000 --- a/DI/Lab.MsDI/WinFormNet48/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln b/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln deleted file mode 100644 index 7a83e88b..00000000 --- a/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30503.244 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNetCore31", "WebApiNetCore31\WebApiNetCore31.csproj", "{B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNet48", "WebApiNet48\WebApiNet48.csproj", "{9EA9B67E-7812-41CB-899B-4331B5344882}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Release|Any CPU.Build.0 = Release|Any CPU - {9EA9B67E-7812-41CB-899B-4331B5344882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9EA9B67E-7812-41CB-899B-4331B5344882}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9EA9B67E-7812-41CB-899B-4331B5344882}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9EA9B67E-7812-41CB-899B-4331B5344882}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {526A154E-E406-4F6B-A76D-4455CA7B02B1} - EndGlobalSection -EndGlobal diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs deleted file mode 100644 index ea9b067e..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Http.Dependencies; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public class DefaultDependencyResolver : IDependencyResolver - { - protected IServiceProvider ServiceProvider { get; set; } - - public DefaultDependencyResolver(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - public object GetService(Type serviceType) - { - return this.ServiceProvider.GetService(serviceType); - } - - public IEnumerable GetServices(Type serviceType) - { - return this.ServiceProvider.GetServices(serviceType); - } - - public IDependencyScope BeginScope() - { - return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); - } - - public void Dispose() - { - // you can implement this interface just when you use .net core 2.0 - // this.ServiceProvider.Dispose(); - ((ServiceProvider)this.ServiceProvider).Dispose(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs deleted file mode 100644 index 343599eb..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Web.Http; -using System.Web.Http.Controllers; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public class DependencyInjectionConfig - { - public static void Register(HttpConfiguration config) - { - var services = ConfigureServices(); - var builder = ConfigureContainerBuilder(services); - var provider = new AutofacServiceProvider(builder.Build()); - - //var provider = services.BuildServiceProvider(); - - var resolver = new DefaultDependencyResolver(provider); - config.DependencyResolver = resolver; - } - - /// - /// 使用 Autofac 註冊 - /// - /// - /// - private static ContainerBuilder ConfigureContainerBuilder(IServiceCollection services) - { - var builder = new ContainerBuilder(); - builder.Populate(services); - - var assembly = Assembly.GetExecutingAssembly(); - builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); - - return builder; - } - - /// - /// 使用 MS DI 註冊 - /// - /// - private static ServiceCollection ConfigureServices() - { - var services = new ServiceCollection(); - - //使用 Microsoft.Extensions.DependencyInjection 註冊 - services.AddControllersAsServices(typeof(DependencyInjectionConfig) - .Assembly - .GetExportedTypes() - .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition) - .Where(t => typeof(IHttpController).IsAssignableFrom(t) - || t.Name.EndsWith("Controller", - StringComparison.OrdinalIgnoreCase))); - - //services.AddScoped(); - - //services.AddTransient() - // .AddSingleton() - // .AddScoped(); - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs deleted file mode 100644 index 614d3991..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public static class ServiceProviderExtensions - { - public static IServiceCollection AddControllersAsServices(this IServiceCollection services, - IEnumerable controllerTypes) - { - foreach (var type in controllerTypes) - { - services.AddTransient(type); - } - - return services; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs deleted file mode 100644 index cd67649d..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Http; - -namespace WebApiNet48 -{ - public static class WebApiConfig - { - public static void Register(HttpConfiguration config) - { - DependencyInjectionConfig.Register(config); - // Web API configuration and services - - // Web API routes - config.MapHttpAttributeRoutes(); - - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new { id = RouteParameter.Optional } - ); - } - } -} diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs deleted file mode 100644 index a2030733..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Net.Http; -using System.Web.Http; - -namespace WebApiNet48.Controllers -{ - public class DefaultController : ApiController - { - private IMessager Messager { get; set; } - - public DefaultController(IMessager messager) - { - this.Messager = messager; - } - - [HttpGet] - public IHttpActionResult Get() - { - var content = $"Messager:{this.Messager.OperationId}"; - return this.Ok(content); - } - - [HttpGet] - public IHttpActionResult Get1() - { - var messager = InstanceManager.Messager; - - var content = $"Messager:{messager.OperationId}"; - return this.Ok(content); - } - } - - public class InstanceManager - { - public static IMessager Messager { get; set; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax deleted file mode 100644 index 593fb3aa..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="WebApiNet48.WebApiApplication" Language="C#" %> diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs deleted file mode 100644 index d3bb6c7b..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Http; -using System.Web.Routing; - -namespace WebApiNet48 -{ - public class WebApiApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - } - } -} diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs deleted file mode 100644 index 137ae92c..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace WebApiNet48 -{ - public interface IMessager - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs deleted file mode 100644 index 91e273eb..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNet48 -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs deleted file mode 100644 index e6621e86..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("WebApiNet48")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WebApiNet48")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9ea9b67e-7812-41cb-899b-4331b5344882")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs deleted file mode 100644 index 7f5ea374..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; - -namespace WebApiNet48 -{ - public static class ServiceCollectionExtensions - { - /// - /// Adds the to the service collection. ONLY FOR PRE-ASP.NET 3.0 HOSTING. THIS WON'T WORK - /// FOR ASP.NET CORE 3.0+ OR GENERIC HOSTING. - /// - /// The service collection to add the factory to. - /// Action on a that adds component registrations to the container. - /// The service collection. - public static IServiceCollection AddAutofac(this IServiceCollection services, Action configurationAction = null) - { - return services.AddSingleton>(new AutofacServiceProviderFactory(configurationAction)); - } - } - -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config deleted file mode 100644 index fae9cfef..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config deleted file mode 100644 index da6e960b..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config deleted file mode 100644 index 89be7bd4..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj deleted file mode 100644 index f8a73be7..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj +++ /dev/null @@ -1,184 +0,0 @@ - - - - - Debug - AnyCPU - - - 2.0 - {9EA9B67E-7812-41CB-899B-4331B5344882} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - WebApiNet48 - WebApiNet48 - v4.8 - true - - 44327 - - - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - true - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - ..\packages\Autofac.6.0.0\lib\netstandard2.0\Autofac.dll - - - ..\packages\Autofac.Extensions.DependencyInjection.7.1.0\lib\netstandard2.0\Autofac.Extensions.DependencyInjection.dll - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - - - ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll - - - ..\packages\System.Diagnostics.DiagnosticSource.4.7.1\lib\net46\System.Diagnostics.DiagnosticSource.dll - - - ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll - - - - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - - - - - - - - - - - - - - - - - - - ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll - - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - - - - - - - - - - - - - - - - Global.asax - - - - - - - Web.config - - - Web.config - - - - - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 54526 - / - https://localhost:44327/ - False - False - - - False - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings deleted file mode 100644 index 8a5228f0..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config b/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config deleted file mode 100644 index f90d917a..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs deleted file mode 100644 index 3aa6d5a3..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace WebApiNetCore31.Controllers -{ - [ApiController] - [Route("[controller]")] - public class DefaultController : ControllerBase - { - private IMessager Messager { get; } - - private readonly ILogger _logger; - - public DefaultController(ILogger logger, - IMessager messager - ) - { - this._logger = logger; - this.Messager = messager; - } - - [HttpGet] - public IActionResult Get() - { - var content = $"Messager:{this.Messager.OperationId}"; - this._logger.LogInformation("Messager:{message}", content); - return this.Ok(content); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs deleted file mode 100644 index 07685fa7..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface IMessager - { - string OperationId { get; } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs deleted file mode 100644 index 101c08fa..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface IScopeMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs deleted file mode 100644 index e803b8c9..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface ISingleMessager : IMessager - { - } -} diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs deleted file mode 100644 index b152a276..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebApiNetCore31 -{ - public interface ITransientMessager : IMessager - { - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs deleted file mode 100644 index 633adcba..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - internal class LogMessager : IMessager - { - public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs deleted file mode 100644 index d3e4038f..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - internal class MachineMessager : IMessager - { - public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs deleted file mode 100644 index e4eb78a9..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace WebApiNetCore31 -{ - public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager - { - public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs deleted file mode 100644 index 9ff36d18..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace WebApiNetCore31 -{ - public class Program - { - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - //.ConfigureServices(services => services.AddAutofac()) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); - } - - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json deleted file mode 100644 index 21810b86..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:54205", - "sslPort": 44308 - } - }, - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "default", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "WebApiNetCore31": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "weatherforecast", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs deleted file mode 100644 index 255e068b..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Reflection; -using Autofac; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace WebApiNetCore31 -{ - public class Startup - { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - - public void ConfigureContainer(ContainerBuilder builder) - { - var assembly = Assembly.GetExecutingAssembly(); - builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj deleted file mode 100644 index 3172ea45..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings deleted file mode 100644 index 8a5228f0..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json deleted file mode 100644 index 8983e0fc..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json deleted file mode 100644 index d9d9a9bf..00000000 --- a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/DI/Lab.MultipleImpl/Client/Client.csproj b/DI/Lab.MultipleImpl/Client/Client.csproj deleted file mode 100644 index e7e23018..00000000 --- a/DI/Lab.MultipleImpl/Client/Client.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net5.0 - - false - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs deleted file mode 100644 index 0dcc77b8..00000000 --- a/DI/Lab.MultipleImpl/Client/UnitTest1.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Autofac.Features.AttributeFilters; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Server; -using Server.Controllers; -using Unity; -using Unity.Microsoft.DependencyInjection; - -namespace Client -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void Autofac注入ServiceName() - { - var hostBuilder = WebHost.CreateDefaultBuilder() - // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureServices(services => { services.AddAutofac(); }) - .UseStartup() - ; - using var server = new TestServer(hostBuilder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "autofac"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void Unity注入ServiceName() - { - var unityContainer = new UnityContainer(); - ConfigureContainer(unityContainer); - - using var server = - new TestServer(WebHost.CreateDefaultBuilder() - .UseStartup() - .UseUnityServiceProvider(unityContainer) - .ConfigureServices(UseUnityController) - ) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "unity"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void 注入FuncName() - { - using var server = - new TestServer(WebHost.CreateDefaultBuilder() - .UseStartup() - .ConfigureServices(UseFuncName) - ) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "default/zip"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - private static void ConfigureContainer(ContainerBuilder builder) - { - // builder.RegisterType().Keyed("file"); - // builder.RegisterType().Keyed("zip"); - // builder.RegisterType().WithAttributeFiltering(); - } - - private static void ConfigureContainer(IUnityContainer container) - { - container.RegisterType("zip"); - container.RegisterType("file"); - } - - private static void UseFuncName(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton>(provider => - key => - { - switch (key) - { - case "zip": - return provider - .GetService(); - case "file": - return provider - .GetService(); - default: - throw new NotSupportedException(); - } - }); - } - - private static void UseUnityController(IServiceCollection services) - { - services.AddControllers() - .AddControllersAsServices(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln deleted file mode 100644 index 8a158dd3..00000000 --- a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{DAE7F74D-E847-4B2F-8930-59AF2698FD1D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NET5.TestProject", "NET5.TestProject\NET5.TestProject.csproj", "{A433C8F8-3B75-412E-955F-287639C55C5F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.Build.0 = Release|Any CPU - {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.Build.0 = Release|Any CPU - {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs deleted file mode 100644 index a1721c5a..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Autofac; -using Autofac.Features.AttributeFilters; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using NET5.TestProject.Controllers; -using NET5.TestProject.File; - -namespace NET5.TestProject -{ - public class AutofacStartup - { - public AutofacStartup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureContainer(ContainerBuilder builder) - { - builder.RegisterType().Keyed("file"); - builder.RegisterType().Keyed("zip"); - builder.RegisterType().WithAttributeFiltering();//<-- add line - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers() - .AddControllersAsServices(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs deleted file mode 100644 index 50bdf475..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Autofac.Features.AttributeFilters; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using NET5.TestProject.File; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class AutofacController : ControllerBase - { - private readonly IFileProvider _fileProvider; - - private readonly ILogger _logger; - - public AutofacController(ILogger logger, - [KeyFilter("zip")] IFileProvider fileProvider) - { - this._logger = logger; - this._fileProvider = fileProvider; - var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; - Console.WriteLine(msg); - } - - [HttpGet] - [Route("{key}")] - public IActionResult Get(string key) - { - var serviceProvider = this.HttpContext.RequestServices; - var autofacServiceProvider = (AutofacServiceProvider) serviceProvider; - var fileProvider = autofacServiceProvider.LifetimeScope.ResolveKeyed(key); - return this.Ok(fileProvider.Print()); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs deleted file mode 100644 index fad0093e..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using NET5.TestProject.File; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class DefaultController : ControllerBase - { - private readonly ILogger _logger; - private readonly IFileProvider _fileProvider; - - public DefaultController(ILogger logger, - IFileProvider fileProvider) - { - this._logger = logger; - this._fileProvider = fileProvider; - } - [HttpGet] - public IActionResult Get() - { - // var fileProvider = this.HttpContext.RequestServices.GetService(); - var fileProvider = this._fileProvider; - var result = fileProvider.Print(); - return this.Ok(result); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs deleted file mode 100644 index 43728f63..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using NET5.TestProject.File; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class FuncController : ControllerBase - { - private readonly IFileProvider _fileProvider; - private readonly ILogger _logger; - - public FuncController(ILogger logger, - Func pool) - { - this._fileProvider = pool("zip"); - this._logger = logger; - var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; - Console.WriteLine(msg); - } - - [HttpGet] - [Route("{type}")] - public IActionResult Get(string type) - { - var fileProvider = this.HttpContext.RequestServices.GetService(type); - var result = fileProvider.Print(); - return this.Ok(result); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs deleted file mode 100644 index 48942f3f..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using NET5.TestProject.File; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class MultiController : ControllerBase - { - private readonly IFileProvider _fileProvider; - - private readonly ILogger _logger; - - // public MultiController(ILogger logger, - // IEnumerable pool) - // { - // this._logger = logger; - // this._fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); - // var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; - // Console.WriteLine(msg); - // } - // - // [HttpGet] - // public IActionResult Get() - // { - // var serviceProvider = this.HttpContext.RequestServices; - // var pool = serviceProvider.GetServices(); - // var fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); - // return this.Ok(fileProvider.Print()); - // } - - public MultiController(ILogger logger, - Dictionary pool) - { - this._logger = logger; - this._fileProvider = pool["zip"]; - var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; - Console.WriteLine(msg); - } - - [HttpGet] - [Route("{key}")] - public IActionResult Get(string key) - { - var serviceProvider = this.HttpContext.RequestServices; - var pool = serviceProvider.GetService>(); - var fileProvider = pool[key]; - return this.Ok(fileProvider.Print()); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs deleted file mode 100644 index c94576a0..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using NET5.TestProject.File; -using Unity; -using Unity.Microsoft.DependencyInjection; - -namespace NET5.TestProject.Controllers -{ - [ApiController] - [Route("[controller]")] - public class UnityController : ControllerBase - { - private readonly IFileProvider _fileProvider; - - private readonly ILogger _logger; - - public UnityController(ILogger logger, - [Dependency("zip")] IFileProvider fileProvider) - { - this._logger = logger; - this._fileProvider = fileProvider; - var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; - Console.WriteLine(msg); - } - - [HttpGet] - [Route("{key}")] - public IActionResult Get(string key) - { - var serviceProvider = this.HttpContext.RequestServices; - var unityServiceProvider = (ServiceProvider) serviceProvider; - var unityContainer = (UnityContainer) unityServiceProvider; - var fileProvider = unityContainer.Resolve(key); - var result = fileProvider.Print(); - return this.Ok(result); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs deleted file mode 100644 index f9ea7bea..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace NET5.TestProject.File -{ - public class FileProvider : IFileProvider - { - public string Print() - { - var msg = "FileProvider"; - Console.WriteLine(msg); - return msg; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs deleted file mode 100644 index e72e465a..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NET5.TestProject.File -{ - public interface IFileProvider - { - string Print(); - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs deleted file mode 100644 index 058d383d..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace NET5.TestProject.File -{ - public class ZipFileProvider : IFileProvider - { - public string Print() - { - var msg = "ZipFileProvider"; - Console.WriteLine(msg); - return msg; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs deleted file mode 100644 index 41034a00..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using NET5.TestProject.File; - -namespace NET5.TestProject -{ - public class FileAdapter - { - private readonly IFileProvider _fileProvider; - - public FileAdapter(IFileProvider fileProvider) - { - this._fileProvider = fileProvider; - } - - public string Get() - { - return this._fileProvider.Print(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs deleted file mode 100644 index 5be65458..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using NET5.TestProject.File; - -namespace NET5.TestProject -{ - public class FuncStartup - { - public FuncStartup(IConfiguration configuration) - { - this.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.AddControllers(); - UseFuncName(services); - } - private static void UseFuncName(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton>(provider => - key => - { - switch (key) - { - case "zip": - return provider - .GetService(); - case "file": - return provider - .GetService(); - default: - throw new NotSupportedException(); - } - }); - } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj deleted file mode 100644 index 1c284244..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net5.0 - - false - - - - - - - - - - - - - - - - true - PreserveNewest - PreserveNewest - - - - diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs deleted file mode 100644 index 2c381a2f..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using NET5.TestProject.File; - -namespace NET5.TestProject -{ - public static class ServiceProviderExtension - { - public static T GetService(this IServiceProvider provider, string name) - { - var pool = (Func) provider.GetService(typeof(Func)); - return (T) pool(name); - } - - public static List GetTypesAssignableFrom(this Assembly assembly) - { - return assembly.GetTypesAssignableFrom(typeof(T)); - } - - public static List GetTypesAssignableFrom(this Assembly assembly, Type compareType) - { - var results = new List(); - foreach (var type in assembly.DefinedTypes) - { - if (compareType.IsAssignableFrom(type) - && compareType != type - ) - { - results.Add(type); - } - } - - return results; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs deleted file mode 100644 index 2bad24fc..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace NET5.TestProject -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - this.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.AddControllers(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs deleted file mode 100644 index 6641e17a..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs +++ /dev/null @@ -1,214 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NET5.TestProject.Controllers; -using NET5.TestProject.File; -using Unity; -using Unity.Microsoft.DependencyInjection; - -namespace NET5.TestProject -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void Autofac注入ServiceName() - { - var hostBuilder = WebHost.CreateDefaultBuilder() - - // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .UseStartup() //<-- add line - .ConfigureServices(services => - { - services.AddAutofac(); - services.AddControllers() - .AddControllersAsServices(); //<-- add line - }) - ; - using var server = new TestServer(hostBuilder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "autofac/zip"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void Unity注入ServiceName() - { - var unityContainer = new UnityContainer(); - unityContainer.RegisterType("zip"); - unityContainer.RegisterType("file"); //<-- add line - - var builder = WebHost.CreateDefaultBuilder() - .UseStartup() - .UseUnityServiceProvider(unityContainer) //<-- add line - .ConfigureServices(s => - { - s.AddControllers() - .AddControllersAsServices(); //<-- add line - }) - ; - using var server = new TestServer(builder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "unity/zip"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void 手動註冊() - { - var hostBuilder = - WebHost.CreateDefaultBuilder() - .UseStartup() - .ConfigureServices(s => - { - s.AddSingleton(); - s.AddSingleton(); - s.AddSingleton(p => - { - var fileProvider = p.GetService(); - var logger = - p.GetService>(); - return new DefaultController(logger, fileProvider); - }); - s.AddControllers().AddControllersAsServices(); - }) - ; - using var server = new TestServer(hostBuilder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "default"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void 注入FuncName() - { - var builder = WebHost.CreateDefaultBuilder() - .UseStartup() - .ConfigureServices(s => - { - s.AddSingleton(); - s.AddSingleton(); - s.AddSingleton>(p => - key => - { - switch (key) - { - case "zip": - return p - .GetService(); - case "file": - return p - .GetService(); - default: - throw new NotSupportedException(); - } - }); - }) - ; - using var server = new TestServer(builder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "func/zip"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - [TestMethod] - public void 注入相同的介面() - { - var hostBuilder = WebHost.CreateDefaultBuilder() - .UseStartup() //<-- add line - .ConfigureServices(service => - { - ScanToDictionary(service); - - // AddToDictionary(service); - }) - ; - using var server = new TestServer(hostBuilder) - { - BaseAddress = new Uri("http://localhost:9527") - }; - - var client = server.CreateClient(); - var url = "multi/zip"; - var response = client.GetAsync(url).Result; - response.EnsureSuccessStatusCode(); - - var result = response.Content.ReadAsStringAsync().Result; - Assert.AreEqual("ZipFileProvider", result); - } - - private static void AddToDictionary(IServiceCollection s) - { - s.AddSingleton(); - s.AddSingleton(); - s.AddSingleton(p => - { - var pool = - new Dictionary - { - {"zip", p.GetService()}, - {"file", p.GetService()} - }; - - return pool; - }); - } - - private static void ScanToDictionary(IServiceCollection services) - { - var assembly = Assembly.GetExecutingAssembly(); - assembly.GetTypesAssignableFrom() - .ForEach(t => { services.AddSingleton(t); }); - services.AddSingleton(p => - { - var pool = - new Dictionary - { - {"zip", p.GetService()}, - {"file", p.GetService()} - }; - - return pool; - }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json b/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json deleted file mode 100644 index d9d9a9bf..00000000 --- a/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/DI/Lab.MultipleImpl/Server/AutofacStartup.cs b/DI/Lab.MultipleImpl/Server/AutofacStartup.cs deleted file mode 100644 index 1e93de1e..00000000 --- a/DI/Lab.MultipleImpl/Server/AutofacStartup.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Autofac; -using Autofac.Features.AttributeFilters; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; -using Server.Controllers; - -namespace Server -{ - public class AutofacStartup - { - public AutofacStartup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureContainer(ContainerBuilder builder) - { - builder.RegisterType().Keyed("file"); - builder.RegisterType().Keyed("zip"); - builder.RegisterType().WithAttributeFiltering(); - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers() - .AddControllersAsServices(); - services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs deleted file mode 100644 index 45debe20..00000000 --- a/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Autofac.Features.AttributeFilters; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class AutofacController : ControllerBase - { - private readonly IFileProvider _fileProvider; - - private readonly ILogger _logger; - - // public AutofacDefaultController(ILogger logger) - // { - // this._logger = logger; - // } - - public AutofacController(ILogger logger, - [KeyFilter("zip")] IFileProvider fileProvider) - { - this._logger = logger; - this._fileProvider = fileProvider; - this._fileProvider.Print(); - } - - [HttpGet] - public IActionResult Get() - { - return this.Ok(this._fileProvider.Print()); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs deleted file mode 100644 index 79efc330..00000000 --- a/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class DefaultController : ControllerBase - { - - private readonly ILogger _logger; - - public DefaultController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - [Route("{type}")] - public IActionResult Get(string type) - { - var fileProvider = this.HttpContext.RequestServices.GetService(type); - var result = fileProvider.Print(); - return this.Ok(result); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs deleted file mode 100644 index 44910a17..00000000 --- a/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Unity; - -namespace Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class UnityController : ControllerBase - { - private readonly IFileProvider _fileProvider; - - private readonly ILogger _logger; - - public UnityController(ILogger logger, - [Dependency("zip")] IFileProvider fileProvider) - { - this._logger = logger; - this._fileProvider = fileProvider; - } - - [HttpGet] - public IActionResult Get() - { - var serviceProvider = this.HttpContext.RequestServices; - var unityServiceProvider = (Unity.Microsoft.DependencyInjection.ServiceProvider) serviceProvider; - var unityContainer = (UnityContainer) unityServiceProvider; - var fileProvider = unityContainer.Resolve("zip"); - var result = fileProvider.Print(); - return this.Ok(result); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs b/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs deleted file mode 100644 index 69c4d675..00000000 --- a/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/DependencyConfig.cs b/DI/Lab.MultipleImpl/Server/DependencyConfig.cs deleted file mode 100644 index b6df11a4..00000000 --- a/DI/Lab.MultipleImpl/Server/DependencyConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.OpenApi.Models; - -namespace Server -{ - public class DependencyConfig - { - public static void ConfigureServices(IServiceCollection services) - { - services.AddControllers() - .AddControllersAsServices() - ; - } - public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/FileProvider.cs b/DI/Lab.MultipleImpl/Server/File/FileProvider.cs deleted file mode 100644 index 06045138..00000000 --- a/DI/Lab.MultipleImpl/Server/File/FileProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Server -{ - public class FileProvider : IFileProvider - { - public string Print() - { - var msg = "FileProvider"; - Console.WriteLine(msg); - return msg; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs deleted file mode 100644 index 7c69a1ae..00000000 --- a/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Server -{ - public interface IFileProvider - { - string Print(); - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs deleted file mode 100644 index 804a286a..00000000 --- a/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Server -{ - public class ZipFileProvider : IFileProvider - { - public string Print() - { - var msg = "ZipFileProvider"; - Console.WriteLine(msg); - return msg; - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Program.cs b/DI/Lab.MultipleImpl/Server/Program.cs deleted file mode 100644 index c083a4aa..00000000 --- a/DI/Lab.MultipleImpl/Server/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Server -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json b/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json deleted file mode 100644 index f822a28b..00000000 --- a/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:59369", - "sslPort": 44389 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Server": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj b/DI/Lab.MultipleImpl/Server/Server.csproj deleted file mode 100644 index 153ed02c..00000000 --- a/DI/Lab.MultipleImpl/Server/Server.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net5.0 - - - - - - - - - - - - - - - - - diff --git a/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs deleted file mode 100644 index 601138e2..00000000 --- a/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Server -{ - public static class ServiceProviderExtension - { - public static T GetService(this IServiceProvider provider, string name) - { - var pool = (Func) provider.GetService(typeof(Func)); - return (T) pool(name); - } - } - -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Startup.cs b/DI/Lab.MultipleImpl/Server/Startup.cs deleted file mode 100644 index e7ecb450..00000000 --- a/DI/Lab.MultipleImpl/Server/Startup.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; - -namespace Server -{ - 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.AddControllers(); - services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/WeatherForecast.cs b/DI/Lab.MultipleImpl/Server/WeatherForecast.cs deleted file mode 100644 index 36e011e2..00000000 --- a/DI/Lab.MultipleImpl/Server/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Server -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/appsettings.Development.json b/DI/Lab.MultipleImpl/Server/appsettings.Development.json deleted file mode 100644 index 8983e0fc..00000000 --- a/DI/Lab.MultipleImpl/Server/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/DI/Lab.MultipleImpl/Server/appsettings.json b/DI/Lab.MultipleImpl/Server/appsettings.json deleted file mode 100644 index d9d9a9bf..00000000 --- a/DI/Lab.MultipleImpl/Server/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/DI/Lib.MsDiForScrutor b/DI/Lib.MsDiForScrutor deleted file mode 160000 index f36a5e15..00000000 --- a/DI/Lib.MsDiForScrutor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f36a5e15db26213b8acfd0dcb823cf530996e1be From b364db7f3d1d48a3f7c4c7f5b6295e3327feebf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Thu, 19 Aug 2021 23:57:19 +0800 Subject: [PATCH 117/267] add project --- DI/Lab.MsDI/Lab.MsDI.sln | 49 + .../App_Start/DefaultDependencyResolver.cs | 31 + .../App_Start/DefaultDependencyResolver2.cs | 35 + .../App_Start/DependencyInjectionConfig.cs | 44 + .../Mvc5Net48/App_Start/FilterConfig.cs | 15 + .../Mvc5Net48/App_Start/RouteConfig.cs | 20 + .../App_Start/ServiceProviderExtensions.cs | 27 + .../Controllers/Default1Controller.cs | 28 + .../Controllers/DefaultController.cs | 43 + DI/Lab.MsDI/Mvc5Net48/Global.asax | 1 + DI/Lab.MsDI/Mvc5Net48/Global.asax.cs | 24 + DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs | 40 + DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs | 30 + DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs | 9 + .../Mvc5Net48/Message/IScopeMessager.cs | 6 + .../Mvc5Net48/Message/ISingleMessager.cs | 6 + .../Mvc5Net48/Message/ITransientMessager.cs | 6 + DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs | 14 + .../Mvc5Net48/Message/MachineMessager.cs | 13 + .../Mvc5Net48/Message/MultiMessager.cs | 13 + DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj | 202 + DI/Lab.MsDI/Mvc5Net48/NLog.config | 45 + DI/Lab.MsDI/Mvc5Net48/NLog.xsd | 3644 +++++++++++++++++ .../Mvc5Net48/Properties/AssemblyInfo.cs | 35 + .../Mvc5Net48/ServiceScopeHttpModule.cs | 42 + .../Mvc5Net48/Views/Default/Index.cshtml | 18 + .../Mvc5Net48/Views/Default1/Index.cshtml | 18 + DI/Lab.MsDI/Mvc5Net48/Views/web.config | 42 + DI/Lab.MsDI/Mvc5Net48/Web.Debug.config | 30 + DI/Lab.MsDI/Mvc5Net48/Web.Release.config | 31 + DI/Lab.MsDI/Mvc5Net48/Web.config | 51 + DI/Lab.MsDI/Mvc5Net48/packages.config | 16 + .../App_Start/DefaultDependencyResolver.cs | 40 + .../App_Start/DependencyInjectionConfig.cs | 40 + .../App_Start/ServiceProviderExtensions.cs | 27 + .../WebApiNet48/App_Start/WebApiConfig.cs | 23 + .../Controllers/Default1Controller.cs | 27 + .../Controllers/DefaultController.cs | 45 + DI/Lab.MsDI/WebApiNet48/Global.asax | 1 + DI/Lab.MsDI/WebApiNet48/Global.asax.cs | 14 + DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs | 28 + DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs | 9 + .../WebApiNet48/Message/IScopeMessager.cs | 6 + .../WebApiNet48/Message/ISingleMessager.cs | 6 + .../WebApiNet48/Message/ITransientMessager.cs | 6 + .../WebApiNet48/Message/LogMessager.cs | 14 + .../WebApiNet48/Message/MachineMessager.cs | 14 + .../WebApiNet48/Message/MultiMessager.cs | 14 + DI/Lab.MsDI/WebApiNet48/NLog.config | 45 + DI/Lab.MsDI/WebApiNet48/NLog.xsd | 3644 +++++++++++++++++ .../WebApiNet48/Properties/AssemblyInfo.cs | 35 + .../ServiceProviderDependencyResolver.cs | 50 + DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs | 42 + DI/Lab.MsDI/WebApiNet48/Web.Debug.config | 30 + DI/Lab.MsDI/WebApiNet48/Web.Release.config | 31 + DI/Lab.MsDI/WebApiNet48/Web.config | 54 + DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj | 186 + .../WebApiNet48.csproj.DotSettings | 2 + DI/Lab.MsDI/WebApiNet48/packages.config | 17 + .../Controllers/Default1Controller.cs | 35 + .../Controllers/DefaultController.cs | 44 + .../WebApiNetCore31/LogFilterAttribute.cs | 27 + .../WebApiNetCore31/Message/IMessager.cs | 9 + .../WebApiNetCore31/Message/IScopeMessager.cs | 6 + .../Message/ISingleMessager.cs | 6 + .../Message/ITransientMessager.cs | 6 + .../WebApiNetCore31/Message/LogMessager.cs | 14 + .../Message/MachineMessager.cs | 13 + .../WebApiNetCore31/Message/MultiMessager.cs | 14 + DI/Lab.MsDI/WebApiNetCore31/Program.cs | 26 + .../Properties/launchSettings.json | 30 + DI/Lab.MsDI/WebApiNetCore31/Startup.cs | 49 + .../WebApiNetCore31/WeatherForecast.cs | 15 + .../WebApiNetCore31/WebApiNetCore31.csproj | 8 + .../WebApiNetCore31.csproj.DotSettings | 2 + DI/Lab.MsDI/WebApiNetCore31/Worker.cs | 22 + .../appsettings.Development.json | 9 + DI/Lab.MsDI/WebApiNetCore31/appsettings.json | 10 + DI/Lab.MsDI/WebApiOwinNet48/App.config | 14 + DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs | 8 + .../App_Start/DefaultDependencyResolver.cs | 39 + .../App_Start/DependencyInjectionConfig.cs | 34 + .../App_Start/ServiceProviderExtensions.cs | 27 + .../WebApiOwinNet48/App_Start/WebApiConfig.cs | 23 + DI/Lab.MsDI/WebApiOwinNet48/Commander.cs | 15 + .../Controllers/DefaultController.cs | 32 + DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs | 69 + DI/Lab.MsDI/WebApiOwinNet48/Program.cs | 18 + .../Properties/AssemblyInfo.cs | 36 + DI/Lab.MsDI/WebApiOwinNet48/Startup.cs | 19 + .../WebApiOwinNet48/WebApiOwinNet48.csproj | 111 + DI/Lab.MsDI/WebApiOwinNet48/packages.config | 18 + DI/Lab.MsDI/WinFormNet48/App.config | 18 + .../WinFormNet48/DependencyInjectionConfig.cs | 64 + DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs | 61 + DI/Lab.MsDI/WinFormNet48/Form1.cs | 21 + DI/Lab.MsDI/WinFormNet48/Form1.resx | 120 + DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs | 7 + .../WinFormNet48/Message/IScopeMessager.cs | 6 + .../WinFormNet48/Message/ISingleMessager.cs | 6 + .../Message/ITransientMessager.cs | 6 + .../WinFormNet48/Message/LogMessager.cs | 9 + .../WinFormNet48/Message/MachineMessager.cs | 9 + .../WinFormNet48/Message/MultiMessager.cs | 9 + DI/Lab.MsDI/WinFormNet48/Program.cs | 26 + .../WinFormNet48/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 + .../WinFormNet48/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 30 + .../WinFormNet48/Properties/Settings.settings | 7 + DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj | 113 + .../WinFormNet48.csproj.DotSettings | 2 + DI/Lab.MsDI/WinFormNet48/Worker.cs | 25 + DI/Lab.MsDI/WinFormNet48/Workflow.cs | 16 + DI/Lab.MsDI/WinFormNet48/packages.config | 8 + DI/Lab.MultipleImpl/Client/Client.csproj | 23 + DI/Lab.MultipleImpl/Client/UnitTest1.cs | 128 + DI/Lab.MultipleImpl/Lab.MultipleImpl.sln | 28 + .../NET5.TestProject/AutofacStartup.cs | 53 + .../Controllers/AutofacController.cs | 38 + .../Controllers/DefaultController.cs | 30 + .../Controllers/FuncController.cs | 33 + .../Controllers/MultiController.cs | 56 + .../Controllers/UnityController.cs | 39 + .../NET5.TestProject/File/FileProvider.cs | 14 + .../NET5.TestProject/File/IFileProvider.cs | 7 + .../NET5.TestProject/File/ZipFileProvider.cs | 14 + .../NET5.TestProject/FileAdapter.cs | 19 + .../NET5.TestProject/FuncStartup.cs | 63 + .../NET5.TestProject/NET5.TestProject.csproj | 28 + .../ServiceProviderExtension.cs | 37 + .../NET5.TestProject/Startup.cs | 41 + .../NET5.TestProject/UnitTest1.cs | 214 + .../NET5.TestProject/appsettings.json | 10 + DI/Lab.MultipleImpl/Server/AutofacStartup.cs | 63 + .../Server/Controllers/AutofacController.cs | 35 + .../Server/Controllers/DefaultController.cs | 31 + .../Server/Controllers/UnityController.cs | 33 + .../Controllers/WeatherForecastController.cs | 39 + .../Server/DependencyConfig.cs | 35 + .../Server/File/FileProvider.cs | 14 + .../Server/File/IFileProvider.cs | 7 + .../Server/File/ZipFileProvider.cs | 14 + DI/Lab.MultipleImpl/Server/Program.cs | 25 + .../Server/Properties/launchSettings.json | 31 + DI/Lab.MultipleImpl/Server/Server.csproj | 21 + .../Server/Server.csproj.DotSettings | 2 + .../Server/ServiceProviderExtension.cs | 14 + DI/Lab.MultipleImpl/Server/Startup.cs | 52 + DI/Lab.MultipleImpl/Server/WeatherForecast.cs | 15 + .../Server/appsettings.Development.json | 9 + DI/Lab.MultipleImpl/Server/appsettings.json | 10 + DI/Lib.MsDiForScrutor/Lib.MsDiForScrutor.sln | 25 + .../Controllers/Default1Controller.cs | 36 + .../Controllers/DefaultController.cs | 43 + .../WebApiNetCore31/Message/IMessager.cs | 7 + .../WebApiNetCore31/Message/IScopeMessager.cs | 6 + .../Message/ISingleMessager.cs | 6 + .../Message/ITransientMessager.cs | 6 + .../WebApiNetCore31/Message/LogMessager.cs | 9 + .../Message/MachineMessager.cs | 9 + .../WebApiNetCore31/Message/Messager.cs | 9 + .../WebApiNetCore31/Message/MultiMessager.cs | 9 + .../WebApiNetCore31/Program.cs | 26 + .../Properties/launchSettings.json | 30 + .../WebApiNetCore31/Startup.cs | 72 + .../WebApiNetCore31/WeatherForecast.cs | 15 + .../WebApiNetCore31/WebApiNetCore31.csproj | 21 + .../WebApiNetCore31/Worker.cs | 22 + .../appsettings.Development.json | 9 + .../WebApiNetCore31/appsettings.json | 10 + 171 files changed, 12368 insertions(+) create mode 100644 DI/Lab.MsDI/Lab.MsDI.sln create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Global.asax create mode 100644 DI/Lab.MsDI/Mvc5Net48/Global.asax.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj create mode 100644 DI/Lab.MsDI/Mvc5Net48/NLog.config create mode 100644 DI/Lab.MsDI/Mvc5Net48/NLog.xsd create mode 100644 DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs create mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml create mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml create mode 100644 DI/Lab.MsDI/Mvc5Net48/Views/web.config create mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.Debug.config create mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.Release.config create mode 100644 DI/Lab.MsDI/Mvc5Net48/Web.config create mode 100644 DI/Lab.MsDI/Mvc5Net48/packages.config create mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Global.asax create mode 100644 DI/Lab.MsDI/WebApiNet48/Global.asax.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/NLog.config create mode 100644 DI/Lab.MsDI/WebApiNet48/NLog.xsd create mode 100644 DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs create mode 100644 DI/Lab.MsDI/WebApiNet48/Web.Debug.config create mode 100644 DI/Lab.MsDI/WebApiNet48/Web.Release.config create mode 100644 DI/Lab.MsDI/WebApiNet48/Web.config create mode 100644 DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj create mode 100644 DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings create mode 100644 DI/Lab.MsDI/WebApiNet48/packages.config create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Program.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Startup.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj create mode 100644 DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings create mode 100644 DI/Lab.MsDI/WebApiNetCore31/Worker.cs create mode 100644 DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json create mode 100644 DI/Lab.MsDI/WebApiNetCore31/appsettings.json create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App.config create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Commander.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Program.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/Startup.cs create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj create mode 100644 DI/Lab.MsDI/WebApiOwinNet48/packages.config create mode 100644 DI/Lab.MsDI/WinFormNet48/App.config create mode 100644 DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Form1.resx create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Program.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx create mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings create mode 100644 DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj create mode 100644 DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings create mode 100644 DI/Lab.MsDI/WinFormNet48/Worker.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/Workflow.cs create mode 100644 DI/Lab.MsDI/WinFormNet48/packages.config create mode 100644 DI/Lab.MultipleImpl/Client/Client.csproj create mode 100644 DI/Lab.MultipleImpl/Client/UnitTest1.cs create mode 100644 DI/Lab.MultipleImpl/Lab.MultipleImpl.sln create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs create mode 100644 DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json create mode 100644 DI/Lab.MultipleImpl/Server/AutofacStartup.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs create mode 100644 DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs create mode 100644 DI/Lab.MultipleImpl/Server/DependencyConfig.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/FileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/IFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs create mode 100644 DI/Lab.MultipleImpl/Server/Program.cs create mode 100644 DI/Lab.MultipleImpl/Server/Properties/launchSettings.json create mode 100644 DI/Lab.MultipleImpl/Server/Server.csproj create mode 100644 DI/Lab.MultipleImpl/Server/Server.csproj.DotSettings create mode 100644 DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs create mode 100644 DI/Lab.MultipleImpl/Server/Startup.cs create mode 100644 DI/Lab.MultipleImpl/Server/WeatherForecast.cs create mode 100644 DI/Lab.MultipleImpl/Server/appsettings.Development.json create mode 100644 DI/Lab.MultipleImpl/Server/appsettings.json create mode 100644 DI/Lib.MsDiForScrutor/Lib.MsDiForScrutor.sln create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/Default1Controller.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/DefaultController.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IScopeMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ISingleMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ITransientMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/LogMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MachineMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/Messager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MultiMessager.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Program.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Properties/launchSettings.json create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Startup.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/WeatherForecast.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/WebApiNetCore31.csproj create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/Worker.cs create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.Development.json create mode 100644 DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.json diff --git a/DI/Lab.MsDI/Lab.MsDI.sln b/DI/Lab.MsDI/Lab.MsDI.sln new file mode 100644 index 00000000..fa493253 --- /dev/null +++ b/DI/Lab.MsDI/Lab.MsDI.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormNet48", "WinFormNet48\WinFormNet48.csproj", "{FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiNetCore31", "WebApiNetCore31\WebApiNetCore31.csproj", "{08C0CD22-F32B-4E54-BC60-AB6912C8D84F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNet48", "WebApiNet48\WebApiNet48.csproj", "{429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mvc5Net48", "Mvc5Net48\Mvc5Net48.csproj", "{91B2AC8D-1516-4D84-AF08-D72A50854607}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiOwinNet48", "WebApiOwinNet48\WebApiOwinNet48.csproj", "{680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8}.Release|Any CPU.Build.0 = Release|Any CPU + {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08C0CD22-F32B-4E54-BC60-AB6912C8D84F}.Release|Any CPU.Build.0 = Release|Any CPU + {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8}.Release|Any CPU.Build.0 = Release|Any CPU + {91B2AC8D-1516-4D84-AF08-D72A50854607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91B2AC8D-1516-4D84-AF08-D72A50854607}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91B2AC8D-1516-4D84-AF08-D72A50854607}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91B2AC8D-1516-4D84-AF08-D72A50854607}.Release|Any CPU.Build.0 = Release|Any CPU + {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49F395E2-6467-4209-955A-D361369FCEA6} + EndGlobalSection +EndGlobal diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs new file mode 100644 index 00000000..ab391905 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; + +namespace Mvc5Net48 +{ + internal class DefaultDependencyResolver : IDependencyResolver + { + public object GetService(Type serviceType) + { + if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) + { + return scope.ServiceProvider.GetService(serviceType); + } + + throw new InvalidOperationException("IServiceScope not provided"); + } + + public IEnumerable GetServices(Type serviceType) + { + if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) + { + return scope.ServiceProvider.GetServices(serviceType); + } + + throw new InvalidOperationException("IServiceScope not provided"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs new file mode 100644 index 00000000..0336d89b --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/DefaultDependencyResolver2.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; + +namespace Mvc5Net48 +{ + /// + /// Scope 的生命週期錯誤 + /// + public class DefaultDependencyResolver2 : IDependencyResolver + { + private readonly ServiceProvider _serviceProvider; + + public DefaultDependencyResolver2(IServiceProvider serviceProvider) + { + this._serviceProvider = serviceProvider as ServiceProvider; + } + + public object GetService(Type serviceType) + { + return this._serviceProvider.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return this._serviceProvider.GetServices(serviceType); + } + + public void Dispose() + { + this._serviceProvider?.Dispose(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs new file mode 100644 index 00000000..2c1a5ccf --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/DependencyInjectionConfig.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Mvc5Net48.Message; + +namespace Mvc5Net48 +{ + public class DependencyInjectionConfig + { + public static void Register() + { + var services = ConfigureServices(); + + var provider = services.BuildServiceProvider(); + + var resolver = new DefaultDependencyResolver(); + ServiceScopeHttpModule.SetServiceProvider(provider); + DependencyResolver.SetResolver(resolver); + + //config.DependencyResolver = resolver; + } + + /// + /// 使用 MS DI 註冊 + /// + /// + private static ServiceCollection ConfigureServices() + { + var services = new ServiceCollection(); + + //使用 Microsoft.Extensions.DependencyInjection 註冊 + services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly + .GetExportedTypes()); + + services.AddScoped(); + + services.AddTransient() + .AddSingleton() + .AddScoped(); + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs new file mode 100644 index 00000000..921340fc --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/FilterConfig.cs @@ -0,0 +1,15 @@ +using System.Web.Mvc; + +namespace Mvc5Net48 +{ + public class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + + // filters.Add(new LogFilterAttribute()); + filters.Add(new LogFilterAttribute2()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs new file mode 100644 index 00000000..c951f437 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/RouteConfig.cs @@ -0,0 +1,20 @@ +using System.Web.Mvc; +using System.Web.Routing; + +namespace Mvc5Net48 +{ + public class RouteConfig + { + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + + } + } +} diff --git a/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs new file mode 100644 index 00000000..a346c1b6 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/App_Start/ServiceProviderExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; + +namespace Mvc5Net48 +{ + public static class ServiceProviderExtensions + { + public static IServiceCollection AddControllersAsServices(this IServiceCollection services, + IEnumerable controllerTypes) + { + var filter = controllerTypes.Where(t => !t.IsAbstract + && !t.IsGenericTypeDefinition) + .Where(t => typeof(ControllerBase).IsAssignableFrom(t) + || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); + + foreach (var type in filter) + { + services.AddTransient(type); + } + + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs b/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs new file mode 100644 index 00000000..567997d6 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Controllers/Default1Controller.cs @@ -0,0 +1,28 @@ +using System; +using System.Web.Mvc; +using Mvc5Net48.Message; +using NLog; + +namespace Mvc5Net48.Controllers +{ + public class Default1Controller : Controller + { + // GET: Default + public ActionResult Index() + { + var single = this.Resolver.GetService(typeof(ISingleMessager)) as ISingleMessager; + var scope = this.Resolver.GetService(typeof(IScopeMessager)) as IScopeMessager; + var transient = this.Resolver.GetService(typeof(ITransientMessager)) as ITransientMessager; + var content = "我在 Controller.Get Action\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}\r\n" + ; + Console.WriteLine(content); + this.ViewBag.Message = content; + var logger = LogManager.GetCurrentClassLogger(); + logger.Trace(content); + return this.View(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs b/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs new file mode 100644 index 00000000..3f17c0d1 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Controllers/DefaultController.cs @@ -0,0 +1,43 @@ +using System; +using System.Web.Mvc; +using Mvc5Net48.Message; +using NLog; + +namespace Mvc5Net48.Controllers +{ + public class DefaultController : Controller + { + private ITransientMessager Transient { get; } + + private IScopeMessager Scope { get; } + + private ISingleMessager Single { get; } + + public DefaultController(ITransientMessager transient, + IScopeMessager scope, + ISingleMessager single) + { + this.Transient = transient; + this.Scope = scope; + this.Single = single; + } + + // GET: Default + public ActionResult Index() + { + var single = this.Single; + var scope = this.Scope; + var transient = this.Transient; + var content = "我在 Controller.Get Action\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}\r\n" + ; + Console.WriteLine(content); + this.ViewBag.Message = content; + var logger = LogManager.GetCurrentClassLogger(); + logger.Trace(content); + return this.View(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Global.asax b/DI/Lab.MsDI/Mvc5Net48/Global.asax new file mode 100644 index 00000000..9fa1c945 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="Mvc5Net48.MvcApplication" Language="C#" %> diff --git a/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs b/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs new file mode 100644 index 00000000..e8693b77 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Global.asax.cs @@ -0,0 +1,24 @@ +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using Mvc5Net48; + +[assembly: PreApplicationStartMethod(typeof(MvcApplication), "InitModule")] +namespace Mvc5Net48 +{ + public class MvcApplication : HttpApplication + { + public static void InitModule() + { + RegisterModule(typeof(ServiceScopeHttpModule)); + } + + protected void Application_Start() + { + AreaRegistration.RegisterAllAreas(); + RouteConfig.RegisterRoutes(RouteTable.Routes); + DependencyInjectionConfig.Register(); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs new file mode 100644 index 00000000..8738a24c --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute.cs @@ -0,0 +1,40 @@ +using System; +using System.Diagnostics; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Mvc5Net48.Message; +using NLog; + +namespace Mvc5Net48 +{ + public class LogFilterAttribute : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + var serviceScope = filterContext.HttpContext?.Items[typeof(IServiceScope)] as IServiceScope; + if (serviceScope == null) + { + return; + } + + var serviceProvider = serviceScope.ServiceProvider; + + var transient = serviceProvider.GetService(); + var single = serviceProvider.GetService(); + + var scope = serviceProvider.GetService(); + var scope2 = DependencyResolver.Current.GetService(); + var noeq = scope.OperationId == scope2.OperationId; + Debug.Assert(noeq); + + var logger = LogManager.GetCurrentClassLogger(); + var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}"; + Console.WriteLine(content); + + logger.Trace(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs new file mode 100644 index 00000000..76e285d4 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/LogFilterAttribute2.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics; +using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Mvc5Net48.Message; +using NLog; + +namespace Mvc5Net48 +{ + public class LogFilterAttribute2 : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + // var transientMessager = filterContext.HttpContext.GetService();//失敗 + + var transient = DependencyResolver.Current.GetService(); + var single = DependencyResolver.Current.GetService(); + var scope = DependencyResolver.Current.GetService(); + + var logger = LogManager.GetCurrentClassLogger(); + var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}"; + Console.WriteLine(content); + + logger.Trace(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs new file mode 100644 index 00000000..e3bda957 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/IMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mvc5Net48.Message +{ + public interface IMessager:IDisposable + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs new file mode 100644 index 00000000..3946da29 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace Mvc5Net48.Message +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs new file mode 100644 index 00000000..78d712f3 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace Mvc5Net48.Message +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs new file mode 100644 index 00000000..b83ff166 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace Mvc5Net48.Message +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs new file mode 100644 index 00000000..b40b1319 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/LogMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace Mvc5Net48.Message +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs new file mode 100644 index 00000000..8d91ec1d --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/MachineMessager.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mvc5Net48.Message +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs b/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs new file mode 100644 index 00000000..d2206ff8 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Message/MultiMessager.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mvc5Net48.Message +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj b/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj new file mode 100644 index 00000000..543d462b --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Mvc5Net48.csproj @@ -0,0 +1,202 @@ + + + + + Debug + AnyCPU + + + 2.0 + {91B2AC8D-1516-4D84-AF08-D72A50854607} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Mvc5Net48 + Mvc5Net48 + v4.8 + true + + 44362 + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll + True + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + True + + + ..\packages\NLog.4.7.6\lib\net45\NLog.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + ..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll + + + ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.dll + + + ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.Deployment.dll + + + ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Webpages.Razor.dll + + + ..\packages\Microsoft.AspNet.Webpages.3.2.7\lib\net45\System.Web.Helpers.dll + + + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + ..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + + + + + + + + + + + + + + Global.asax + + + + + + + + + + + + + + + + + PreserveNewest + + + Designer + + + + + + Web.config + + + Web.config + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 60289 + / + https://localhost:44362/ + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/NLog.config b/DI/Lab.MsDI/Mvc5Net48/NLog.config new file mode 100644 index 00000000..cd5f89eb --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/NLog.config @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDI/Mvc5Net48/NLog.xsd b/DI/Lab.MsDI/Mvc5Net48/NLog.xsd new file mode 100644 index 00000000..32246a71 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/NLog.xsd @@ -0,0 +1,3644 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged. + + + + + Throw an exception when there is an internal error. Default value is: false. Not recommend to set to true in production! + + + + + Throw an exception when there is a configuration error. If not set, determined by throwExceptions. + + + + + Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. + + + + + Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. + + + + + Write timestamps for internal NLog messages. Default value is: true. + + + + + Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. + + + + + Perform message template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Filter on the name of the logger. May include wildcard characters ('*' or '?'). + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable this rule. Note: disabled rules aren't available from the API. + + + + + Rule identifier to allow rule lookup with Configuration.FindRuleByName and Configuration.RemoveRuleByName. + + + + + + + + + + + + + + + Default action if none of the filters match. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + + Variable value. Note, the 'value' attribute has precedence over this one. + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Whether to use the locking queue, instead of a lock-free concurrent queue The locking queue is less concurrent when many logger threads, but reduces memory allocation + + + + + Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. (1 or less means trigger on new activity) + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Delay the flush until the LogEvent has been confirmed as written + + + + + Condition expression. Log events who meet this condition will cause a flush on the wrapped target. + + + + + Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Action to take if the buffer overflows. + + + + + Indicates whether to use sliding timeout. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Maximum queue size. + + + + + Maximum current connections. 0 = no maximum. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + NDLC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Renderer for log4j:event logger-xml-attribute (Default ${logger}) + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Viewer parameter name. + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + Enables output using ANSI Color Codes + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + Indicates whether to auto-flush after + + + + + Indicates whether to auto-check if the console has been redirected to file - Disables coloring logic when System.Console.IsOutputRedirected = true + + + + + Indicates whether to use default row highlighting rules. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Condition that must be met before scanning the row for highlight of words + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to auto-flush after + + + + + Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + Whether to enable batch writing using char[]-buffers, instead of using + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Name of the database provider. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Configures isolated transaction batch writing. If supported by the database, then it will improve insert performance. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Convert format of the property value + + + + + Culture used for parsing property string-value for type-conversion + + + + + Value to assign on the object-property + + + + + Name for the object-property + + + + + Type of the object-property + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + + + + + + Database parameter name. + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Database parameter DbType. + + + + + Database parameter size. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Type of the parameter. + + + + + Whether empty value should translate into DbNull. Requires database column to allow NULL values. + + + + + Convert format of the database parameter value. + + + + + Culture used for parsing parameter string-value for type-conversion + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Optional entry type. When not set, or when not convertible to then determined by + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Maximum Event log size in kilobytes. + + + + + Message length limit to write to the Event Log. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Maximum days of archive files that should be kept. + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Is the an absolute or relative path? + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Maximum number of archive files that should be kept. + + + + + Indicates whether the footer should be written only when the file is archived. + + + + + Maximum number of log file names that should be stored as existing. + + + + + Indicates whether to delete old log file on startup. + + + + + File attributes (Windows only). + + + + + Indicates whether to create directories if they do not exist. + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Value of the file size threshold to archive old log file on startup. + + + + + Indicates whether to archive old log file on startup. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + Indicates whether to write BOM (byte order mark) in created files + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether file creation calls should be synchronized by a system global mutex. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + + + + + Is the an absolute or relative path? + + + + + Name of the file to write to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + Log file buffer size in bytes. + + + + + Maximum number of seconds before open files are flushed. If this number is negative or zero the files are not flushed by timer. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interval in which messages will be written up to the number of messages. + + + + + Maximum allowed number of messages written per . + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + Type of the parameter. Obsolete alias for + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Priority used for sending mails. + + + + + Encoding to be used for sending e-mail. + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Indicates whether to add new lines between log entries. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Indicates the SMTP client timeout. + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Max number of items to have in memory + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Maximum queue size. + + + + + Maximum current connections. 0 = no maximum. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + NDLC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Renderer for log4j:event logger-xml-attribute (Default ${logger}) + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Forward to (Instead of ) + + + + + Always use independent of + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Protocol to be used when calling web service. + + + + + Custom proxy address, include port separated by a colon + + + + + Encoding. + + + + + Web service URL. + + + + + Value whether escaping be done according to the old NLog style (Very non-standard) + + + + + Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) + + + + + Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) + + + + + Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). + + + + + (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). + + + + + Proxy configuration when calling web service + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + Override of Quoting mode + + + + + + + + + + + + + + + + + + + + + Should forward slashes be escaped? If true, / will be converted to \/ + + + + + Option to render the empty object value {} + + + + + Option to suppress the extra spaces in the output json + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as JSON) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the JSON serializer follow object references before backing off + + + + + + + + + + + + + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + Determines whether or not this attribute will be Json encoded. + + + + + Should forward slashes be escaped? If true, / will be converted to \/ + + + + + Indicates whether to escape non-ascii characters + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + + + + + + Option to include all properties from the log events + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as XML) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the XML serializer follow object references before backing off + + + + + XML element name to use for rendering IList-collections items + + + + + XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included + + + + + XML element name to use when rendering properties + + + + + XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value + + + + + Name of the root XML element + + + + + Value inside the root XML element + + + + + Whether a ElementValue with empty value should be included in the output + + + + + Auto indent and create new lines + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + + + + + + + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + + + + + + + + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + Name of the element + + + + + Value inside the element + + + + + Whether a ElementValue with empty value should be included in the output + + + + + Auto indent and create new lines + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as XML) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the XML serializer follow object references before backing off + + + + + XML element name to use for rendering IList-collections items + + + + + XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included + + + + + XML element name to use when rendering properties + + + + + XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Default number of unique filter values to expect, will automatically increase if needed + + + + + Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. + + + + + Layout to be used to filter log messages. + + + + + Max number of unique filter values to expect simultaneously + + + + + Max length of filter values, will truncate if above limit + + + + + How long before a filter expires, and logging is accepted again + + + + + Default buffer size for the internal buffers + + + + + Reuse internal buffers, and doesn't have to constantly allocate new buffers + + + + + Append FilterCount to the when an event is no longer filtered + + + + + Insert FilterCount value into when an event is no longer filtered + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..fecc934b --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Mvc5Net48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Mvc5Net48")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("91b2ac8d-1516-4d84-af08-d72a50854607")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs b/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs new file mode 100644 index 00000000..7cdb30a7 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/ServiceScopeHttpModule.cs @@ -0,0 +1,42 @@ +using System; +using System.Web; +using Microsoft.Extensions.DependencyInjection; + +namespace Mvc5Net48 +{ + internal class ServiceScopeHttpModule : IHttpModule + { + private static ServiceProvider s_serviceProvider; + + public static void SetServiceProvider(IServiceProvider serviceProvider) + { + s_serviceProvider = serviceProvider as ServiceProvider; + } + + public void Dispose() + { + s_serviceProvider?.Dispose(); + } + + public void Init(HttpApplication context) + { + context.BeginRequest += this.Context_BeginRequest; + context.EndRequest += this.Context_EndRequest; + } + + private void Context_BeginRequest(object sender, EventArgs e) + { + var context = ((HttpApplication) sender).Context; + context.Items[typeof(IServiceScope)] = s_serviceProvider.CreateScope(); + } + + private void Context_EndRequest(object sender, EventArgs e) + { + var context = ((HttpApplication) sender).Context; + if (context.Items[typeof(IServiceScope)] is IServiceScope scope) + { + scope.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml b/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml new file mode 100644 index 00000000..8ce20645 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Views/Default/Index.cshtml @@ -0,0 +1,18 @@ +@model dynamic + +@{ + Layout = null; +} + + + + + + title + + +
+ @Html.Raw(HttpUtility.HtmlDecode(@ViewBag.Message)) +
+ + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml b/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml new file mode 100644 index 00000000..8ce20645 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Views/Default1/Index.cshtml @@ -0,0 +1,18 @@ +@model dynamic + +@{ + Layout = null; +} + + + + + + title + + +
+ @Html.Raw(HttpUtility.HtmlDecode(@ViewBag.Message)) +
+ + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Views/web.config b/DI/Lab.MsDI/Mvc5Net48/Views/web.config new file mode 100644 index 00000000..3a3275a4 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Views/web.config @@ -0,0 +1,42 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config b/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config new file mode 100644 index 00000000..fae9cfef --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.Release.config b/DI/Lab.MsDI/Mvc5Net48/Web.Release.config new file mode 100644 index 00000000..da6e960b --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/Mvc5Net48/Web.config b/DI/Lab.MsDI/Mvc5Net48/Web.config new file mode 100644 index 00000000..58fb7220 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/Web.config @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDI/Mvc5Net48/packages.config b/DI/Lab.MsDI/Mvc5Net48/packages.config new file mode 100644 index 00000000..138971e0 --- /dev/null +++ b/DI/Lab.MsDI/Mvc5Net48/packages.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs new file mode 100644 index 00000000..e9e64035 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/App_Start/DefaultDependencyResolver.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Web.Http.Dependencies; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public class DefaultDependencyResolver : IDependencyResolver + { + private readonly IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + + public DefaultDependencyResolver(IServiceProvider serviceProvider, IServiceScope serviceScope = null) + { + this._serviceProvider = serviceProvider; + this._serviceScope = serviceScope; + } + + public object GetService(Type serviceType) + { + return this._serviceProvider.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return this._serviceProvider.GetServices(serviceType); + } + + public IDependencyScope BeginScope() + { + this._serviceScope = this._serviceProvider.CreateScope(); + return new DefaultDependencyResolver(this._serviceScope.ServiceProvider,this._serviceScope); + } + + public void Dispose() + { + this._serviceScope?.Dispose(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs new file mode 100644 index 00000000..fee3990d --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/App_Start/DependencyInjectionConfig.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; +using System.Web.Http; +using System.Web.Http.Controllers; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public class DependencyInjectionConfig + { + public static void Register(HttpConfiguration config) + { + var services = ConfigureServices(); + + var provider = services.BuildServiceProvider(); + + var resolver = new DefaultDependencyResolver(provider); + config.DependencyResolver = resolver; + } + + /// + /// 使用 MS DI 註冊 + /// + /// + private static ServiceCollection ConfigureServices() + { + var services = new ServiceCollection(); + + //使用 Microsoft.Extensions.DependencyInjection 註冊 + services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly.GetExportedTypes()); + + services.AddScoped(); + + services.AddTransient() + .AddSingleton() + .AddScoped(); + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs new file mode 100644 index 00000000..ae5524bc --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/App_Start/ServiceProviderExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http.Controllers; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public static class ServiceProviderExtensions + { + public static IServiceCollection AddControllersAsServices(this IServiceCollection services, + IEnumerable controllerTypes) + { + var filter = controllerTypes.Where(t => !t.IsAbstract + && !t.IsGenericTypeDefinition) + .Where(t => typeof(IHttpController).IsAssignableFrom(t) + || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); + + foreach (var type in filter) + { + services.AddTransient(type); + } + + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs new file mode 100644 index 00000000..19e9c7a4 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/App_Start/WebApiConfig.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Web.Http; + +namespace WebApiNet48 +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Web API configuration and services + config.Filters.Add(new LogFilterAttribute()); + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + } + } +} diff --git a/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs b/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs new file mode 100644 index 00000000..ea631cce --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Controllers/Default1Controller.cs @@ -0,0 +1,27 @@ +using System; +using System.Net.Http; +using System.Web.Http; +using NLog; + +namespace WebApiNet48.Controllers +{ + public class Default1Controller : ApiController + { + [HttpGet] + public IHttpActionResult Get() + { + var logger = LogManager.GetCurrentClassLogger(); + var requestScope = this.Request.GetDependencyScope(); + var transient = requestScope.GetService(typeof(ITransientMessager)) as ITransientMessager; + var scope = requestScope.GetService(typeof(IScopeMessager)) as IScopeMessager; + var single = requestScope.GetService(typeof(ISingleMessager)) as ISingleMessager; + var content = "我在 Controller.Get Action\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}"; + Console.WriteLine(content); + logger.Info(content); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs new file mode 100644 index 00000000..51985f63 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Controllers/DefaultController.cs @@ -0,0 +1,45 @@ +using System; +using System.Web.Http; +using NLog; + +namespace WebApiNet48.Controllers +{ + public class DefaultController : ApiController + { + private IMessager Transient { get; } + + private IMessager Scope { get; } + + private IMessager Single { get; } + + public DefaultController(ITransientMessager transient, + IScopeMessager scope, + ISingleMessager single) + { + this.Transient = transient; + this.Scope = scope; + this.Single = single; + } + + [HttpGet] + public IHttpActionResult Get() + { + var logger = LogManager.GetCurrentClassLogger(); + + var content = "我在 Controller.Get Action\r\n" + + $"transient:{this.Transient.OperationId}\r\n" + + $"scope:{this.Scope.OperationId}\r\n" + + $"single:{this.Single.OperationId}"; + Console.WriteLine(content); + logger.Info(content); + + //this._logger.LogInformation("transient = {transient},scope = {scope},single = {single}", + // this.Transient.OperationId, + // this.Scope.OperationId, + // this.Single.OperationId); + return this.Ok(content); + } + + + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Global.asax b/DI/Lab.MsDI/WebApiNet48/Global.asax new file mode 100644 index 00000000..593fb3aa --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="WebApiNet48.WebApiApplication" Language="C#" %> diff --git a/DI/Lab.MsDI/WebApiNet48/Global.asax.cs b/DI/Lab.MsDI/WebApiNet48/Global.asax.cs new file mode 100644 index 00000000..3cb9d613 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Global.asax.cs @@ -0,0 +1,14 @@ +using System.Web; +using System.Web.Http; + +namespace WebApiNet48 +{ + public class WebApiApplication : HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + GlobalConfiguration.Configure(DependencyInjectionConfig.Register); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs b/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs new file mode 100644 index 00000000..f2055f34 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/LogFilterAttribute.cs @@ -0,0 +1,28 @@ +using System; +using System.Net.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Filters; +using NLog; + +namespace WebApiNet48 +{ + public class LogFilterAttribute : ActionFilterAttribute + { + public override void OnActionExecuting(HttpActionContext actionContext) + { + var requestScope = actionContext.Request.GetDependencyScope(); + + var transient = requestScope.GetService(typeof(ITransientMessager)) as MultiMessager; + var scope = requestScope.GetService(typeof(IScopeMessager)) as MultiMessager; + var single = requestScope.GetService(typeof(ISingleMessager)) as MultiMessager; + + var logger = LogManager.GetCurrentClassLogger(); + var content = "我在 LogFilterAttribute.OnActionExecuting\r\n" + + $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}"; + Console.WriteLine(content); + logger.Info(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs new file mode 100644 index 00000000..9883346c --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/IMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNet48 +{ + public interface IMessager:IDisposable + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs new file mode 100644 index 00000000..a29d7677 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNet48 +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs new file mode 100644 index 00000000..27284507 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNet48 +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs new file mode 100644 index 00000000..d9314497 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNet48 +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs new file mode 100644 index 00000000..a73f42f4 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/LogMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace WebApiNet48 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs new file mode 100644 index 00000000..8aed0c19 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/MachineMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace WebApiNet48 +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs new file mode 100644 index 00000000..7e3edf7b --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Message/MultiMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace WebApiNet48 +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/NLog.config b/DI/Lab.MsDI/WebApiNet48/NLog.config new file mode 100644 index 00000000..df272249 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/NLog.config @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDI/WebApiNet48/NLog.xsd b/DI/Lab.MsDI/WebApiNet48/NLog.xsd new file mode 100644 index 00000000..32246a71 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/NLog.xsd @@ -0,0 +1,3644 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged. + + + + + Throw an exception when there is an internal error. Default value is: false. Not recommend to set to true in production! + + + + + Throw an exception when there is a configuration error. If not set, determined by throwExceptions. + + + + + Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false. + + + + + Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false. + + + + + Write timestamps for internal NLog messages. Default value is: true. + + + + + Use InvariantCulture as default culture instead of CurrentCulture. Default value is: false. + + + + + Perform message template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty. + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Filter on the name of the logger. May include wildcard characters ('*' or '?'). + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable this rule. Note: disabled rules aren't available from the API. + + + + + Rule identifier to allow rule lookup with Configuration.FindRuleByName and Configuration.RemoveRuleByName. + + + + + + + + + + + + + + + Default action if none of the filters match. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + + Variable value. Note, the 'value' attribute has precedence over this one. + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Whether to use the locking queue, instead of a lock-free concurrent queue The locking queue is less concurrent when many logger threads, but reduces memory allocation + + + + + Limit of full s to write before yielding into Performance is better when writing many small batches, than writing a single large batch + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. (1 or less means trigger on new activity) + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Delay the flush until the LogEvent has been confirmed as written + + + + + Condition expression. Log events who meet this condition will cause a flush on the wrapped target. + + + + + Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Action to take if the buffer overflows. + + + + + Indicates whether to use sliding timeout. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Maximum queue size. + + + + + Maximum current connections. 0 = no maximum. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + NDLC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Renderer for log4j:event logger-xml-attribute (Default ${logger}) + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Viewer parameter name. + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + Enables output using ANSI Color Codes + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + Indicates whether to auto-flush after + + + + + Indicates whether to auto-check if the console has been redirected to file - Disables coloring logic when System.Console.IsOutputRedirected = true + + + + + Indicates whether to use default row highlighting rules. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Condition that must be met before scanning the row for highlight of words + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to auto-flush after + + + + + Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App) + + + + + The encoding for writing messages to the . + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + Whether to enable batch writing using char[]-buffers, instead of using + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Name of the database provider. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Configures isolated transaction batch writing. If supported by the database, then it will improve insert performance. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Convert format of the property value + + + + + Culture used for parsing property string-value for type-conversion + + + + + Value to assign on the object-property + + + + + Name for the object-property + + + + + Type of the object-property + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + + + + + + Database parameter name. + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Database parameter DbType. + + + + + Database parameter size. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Type of the parameter. + + + + + Whether empty value should translate into DbNull. Requires database column to allow NULL values. + + + + + Convert format of the database parameter value. + + + + + Culture used for parsing parameter string-value for type-conversion + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Optional entry type. When not set, or when not convertible to then determined by + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Maximum Event log size in kilobytes. + + + + + Message length limit to write to the Event Log. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Maximum days of archive files that should be kept. + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Is the an absolute or relative path? + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Maximum number of archive files that should be kept. + + + + + Indicates whether the footer should be written only when the file is archived. + + + + + Maximum number of log file names that should be stored as existing. + + + + + Indicates whether to delete old log file on startup. + + + + + File attributes (Windows only). + + + + + Indicates whether to create directories if they do not exist. + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Value of the file size threshold to archive old log file on startup. + + + + + Indicates whether to archive old log file on startup. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + Indicates whether to write BOM (byte order mark) in created files + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether file creation calls should be synchronized by a system global mutex. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + + + + + Is the an absolute or relative path? + + + + + Name of the file to write to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + Log file buffer size in bytes. + + + + + Maximum number of seconds before open files are flushed. If this number is negative or zero the files are not flushed by timer. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interval in which messages will be written up to the number of messages. + + + + + Maximum allowed number of messages written per . + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + Type of the parameter. Obsolete alias for + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Priority used for sending mails. + + + + + Encoding to be used for sending e-mail. + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Indicates whether to add new lines between log entries. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Indicates the SMTP client timeout. + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Max number of items to have in memory + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + End of line value if a newline is appended at the end of log message . + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + The number of seconds a connection will remain idle before the first keep-alive probe is sent + + + + + Maximum queue size. + + + + + Maximum current connections. 0 = no maximum. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + NDLC item separator. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + Renderer for log4j:event logger-xml-attribute (Default ${logger}) + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include dictionary contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Option to include all properties from the log events + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + NDC item separator. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Forward to (Instead of ) + + + + + Always use independent of + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Protocol to be used when calling web service. + + + + + Custom proxy address, include port separated by a colon + + + + + Encoding. + + + + + Web service URL. + + + + + Value whether escaping be done according to the old NLog style (Very non-standard) + + + + + Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) + + + + + Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in parameters) + + + + + Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see and ). + + + + + (optional) root namespace of the XML document, if POST of XML document chosen. (see and ). + + + + + Proxy configuration when calling web service + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + Override of Quoting mode + + + + + + + + + + + + + + + + + + + + + Should forward slashes be escaped? If true, / will be converted to \/ + + + + + Option to render the empty object value {} + + + + + Option to suppress the extra spaces in the output json + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as JSON) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the JSON serializer follow object references before backing off + + + + + + + + + + + + + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + Determines whether or not this attribute will be Json encoded. + + + + + Should forward slashes be escaped? If true, / will be converted to \/ + + + + + Indicates whether to escape non-ascii characters + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + + + + + + Option to include all properties from the log events + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include contents of the stack. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as XML) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the XML serializer follow object references before backing off + + + + + XML element name to use for rendering IList-collections items + + + + + XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included + + + + + XML element name to use when rendering properties + + + + + XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value + + + + + Name of the root XML element + + + + + Value inside the root XML element + + + + + Whether a ElementValue with empty value should be included in the output + + + + + Auto indent and create new lines + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + + + + + + + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + Whether an attribute with empty value should be included in the output + + + + + + + + + + + + + + + + + + + + + + + + + Determines whether or not this attribute will be Xml encoded. + + + + + Name of the element + + + + + Value inside the element + + + + + Whether a ElementValue with empty value should be included in the output + + + + + Auto indent and create new lines + + + + + List of property names to exclude when is true + + + + + Option to include all properties from the log event (as XML) + + + + + Indicates whether to include contents of the dictionary. + + + + + Indicates whether to include contents of the dictionary. + + + + + How far should the XML serializer follow object references before backing off + + + + + XML element name to use for rendering IList-collections items + + + + + XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included + + + + + XML element name to use when rendering properties + + + + + XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Default number of unique filter values to expect, will automatically increase if needed + + + + + Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout. + + + + + Layout to be used to filter log messages. + + + + + Max number of unique filter values to expect simultaneously + + + + + Max length of filter values, will truncate if above limit + + + + + How long before a filter expires, and logging is accepted again + + + + + Default buffer size for the internal buffers + + + + + Reuse internal buffers, and doesn't have to constantly allocate new buffers + + + + + Append FilterCount to the when an event is no longer filtered + + + + + Insert FilterCount value into when an event is no longer filtered + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f5b17259 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebApiNet48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WebApiNet48")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("429d865c-ede7-4ea1-bc51-9a5bcab26bb8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs b/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs new file mode 100644 index 00000000..ccb1df5c --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/ServiceProviderDependencyResolver.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Http.Dependencies; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + internal class ServiceProviderDependencyResolver : IDependencyResolver + { + protected IServiceProvider ServiceProvider { get; set; } + + public ServiceProviderDependencyResolver(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + public object GetService(Type serviceType) + { + if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) + { + return scope.ServiceProvider.GetService(serviceType); + } + + return this.ServiceProvider.GetService(serviceType); + throw new InvalidOperationException("IServiceScope not provided"); + } + + public IEnumerable GetServices(Type serviceType) + { + if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) + { + return scope.ServiceProvider.GetServices(serviceType); + } + + return this.ServiceProvider.GetServices(serviceType); + throw new InvalidOperationException("IServiceScope not provided"); + } + + public IDependencyScope BeginScope() + { + return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); + } + + public void Dispose() + { + //throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs b/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs new file mode 100644 index 00000000..d326414f --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/ServiceScopeModule.cs @@ -0,0 +1,42 @@ +using System; +using System.Web; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + internal class ServiceScopeModule : IHttpModule + { + private static ServiceProvider _serviceProvider; + + public void Dispose() + { + + } + + public void Init(HttpApplication context) + { + context.BeginRequest += this.Context_BeginRequest; + context.EndRequest += this.Context_EndRequest; + } + + private void Context_EndRequest(object sender, EventArgs e) + { + var context = ((HttpApplication)sender).Context; + if (context.Items[typeof(IServiceScope)] is IServiceScope scope) + { + scope.Dispose(); + } + } + + private void Context_BeginRequest(object sender, EventArgs e) + { + var context = ((HttpApplication)sender).Context; + context.Items[typeof(IServiceScope)] = _serviceProvider.CreateScope(); + } + + public static void SetServiceProvider(ServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.Debug.config b/DI/Lab.MsDI/WebApiNet48/Web.Debug.config new file mode 100644 index 00000000..fae9cfef --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.Release.config b/DI/Lab.MsDI/WebApiNet48/Web.Release.config new file mode 100644 index 00000000..da6e960b --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/Web.config b/DI/Lab.MsDI/WebApiNet48/Web.config new file mode 100644 index 00000000..491d448d --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/Web.config @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj new file mode 100644 index 00000000..32461adc --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj @@ -0,0 +1,186 @@ + + + + + Debug + AnyCPU + + + 2.0 + {429D865C-EDE7-4EA1-BC51-9A5BCAB26BB8} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + WebApiNet48 + WebApiNet48 + v4.8 + true + + 44304 + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\NLog.4.7.5\lib\net45\NLog.dll + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + + + + + + + + + + + Global.asax + + + + + + + + + + + + + + + + PreserveNewest + + + Designer + + + + Web.config + + + Web.config + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 50548 + / + https://localhost:44304/ + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings new file mode 100644 index 00000000..8a5228f0 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/WebApiNet48.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNet48/packages.config b/DI/Lab.MsDI/WebApiNet48/packages.config new file mode 100644 index 00000000..e8fcf0e2 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNet48/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs b/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs new file mode 100644 index 00000000..38dc48ee --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Controllers/Default1Controller.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class Default1Controller : ControllerBase + { + private readonly ILogger _logger; + + public Default1Controller(ILogger logger) + { + this._logger = logger; + } + + [HttpGet] + public IActionResult Get() + { + var serviceProvider = this.HttpContext.RequestServices; + var transient = serviceProvider.GetService(typeof(ITransientMessager)) as ITransientMessager; + var scope = serviceProvider.GetService(typeof(IScopeMessager)) as IScopeMessager; + var single = serviceProvider.GetService(typeof(ISingleMessager)) as ISingleMessager; + var content = $"transient:{transient.OperationId}\r\n" + + $"scope:{scope.OperationId}\r\n" + + $"single:{single.OperationId}"; + + this._logger.LogInformation("transient = {transient},scope = {scope},single = {single}", + transient.OperationId, + scope.OperationId, + single.OperationId); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs new file mode 100644 index 00000000..2fe6ac31 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Controllers/DefaultController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private IMessager Transient { get; } + + private IMessager Scope { get; } + + private IMessager Single { get; } + + private readonly ILogger _logger; + + public DefaultController(ILogger logger, + ITransientMessager transient, + IScopeMessager scope, + ISingleMessager single) + { + this._logger = logger; + + this.Transient = transient; + this.Scope = scope; + this.Single = single; + } + + [HttpGet] + public IActionResult Get() + { + var content = "我在 DefaultController.Get \r\n" + + $"transient:{this.Transient.OperationId}\r\n" + + $"scope:{this.Scope.OperationId}\r\n" + + $"single:{this.Single.OperationId}"; + this._logger.LogInformation("我在 DefaultController.Get ,transient = {transient},scope = {scope},single = {single}", + this.Transient.OperationId, + this.Scope.OperationId, + this.Single.OperationId); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs b/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs new file mode 100644 index 00000000..72b0c840 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/LogFilterAttribute.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31 +{ + public class LogFilterAttribute : ActionFilterAttribute + { + public LogFilterAttribute() + { + + } + public override void OnActionExecuting(ActionExecutingContext context) + { + var transient = context.HttpContext.RequestServices.GetService(); + var scope = context.HttpContext.RequestServices.GetService(); + var single = context.HttpContext.RequestServices.GetService(); + var logger = context.HttpContext.RequestServices.GetService>(); + logger.LogInformation("我在 LogFilterAttribute ,transient = {transient},scope = {scope},single = {single}", + transient.OperationId, + scope.OperationId, + single.OperationId); + + //Console.WriteLine(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs new file mode 100644 index 00000000..479144a9 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/IMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + public interface IMessager:IDisposable + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs new file mode 100644 index 00000000..101c08fa --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs new file mode 100644 index 00000000..e803b8c9 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs new file mode 100644 index 00000000..b152a276 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs new file mode 100644 index 00000000..9819ac66 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/LogMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(LogMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs new file mode 100644 index 00000000..ed49b5dd --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/MachineMessager.cs @@ -0,0 +1,13 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + public void Dispose() + { + Console.WriteLine($"{nameof(MachineMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs new file mode 100644 index 00000000..68182ed0 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Message/MultiMessager.cs @@ -0,0 +1,14 @@ +using System; + +namespace WebApiNetCore31 +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + + public void Dispose() + { + Console.WriteLine($"{nameof(MultiMessager)} GC"); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Program.cs b/DI/Lab.MsDI/WebApiNetCore31/Program.cs new file mode 100644 index 00000000..aba702a0 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json b/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json new file mode 100644 index 00000000..e27ff49d --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55458", + "sslPort": 44311 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "default", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApiNetCore31": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Startup.cs b/DI/Lab.MsDI/WebApiNetCore31/Startup.cs new file mode 100644 index 00000000..60390753 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Startup.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace WebApiNetCore31 +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + //services.AddControllers(); + services.AddControllers(options => options.Filters.Add(new LogFilterAttribute())); + + services.AddTransient(p => new Worker(new LogMessager())); + services.AddTransient(p => new Worker2(new MachineMessager())); + + services.AddTransient() + .AddSingleton() + .AddScoped() + ; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs b/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs new file mode 100644 index 00000000..1bb9e27c --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace WebApiNetCore31 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj new file mode 100644 index 00000000..d12c450b --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj @@ -0,0 +1,8 @@ + + + + netcoreapp3.1 + + + + diff --git a/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings new file mode 100644 index 00000000..8a5228f0 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/Worker.cs b/DI/Lab.MsDI/WebApiNetCore31/Worker.cs new file mode 100644 index 00000000..f069f7e4 --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/Worker.cs @@ -0,0 +1,22 @@ +namespace WebApiNetCore31 +{ + public class Worker + { + public IMessager Messager { get; set; } + + public Worker(IMessager messager) + { + this.Messager = messager; + } + } + + public class Worker2 + { + public IMessager Messager { get; set; } + + public Worker2(IMessager messager) + { + this.Messager = messager; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json b/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/DI/Lab.MsDI/WebApiNetCore31/appsettings.json b/DI/Lab.MsDI/WebApiNetCore31/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lab.MsDI/WebApiNetCore31/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App.config b/DI/Lab.MsDI/WebApiOwinNet48/App.config new file mode 100644 index 00000000..4a79e878 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs b/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs new file mode 100644 index 00000000..cf2c1518 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/AppSetting.cs @@ -0,0 +1,8 @@ +namespace WebApiOwinNet48 +{ + public class ServerSetting + { + public const string HostEndpoint = "http://localhost:8001"; + + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs new file mode 100644 index 00000000..d4d14983 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DefaultDependencyResolver.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Web.Http.Dependencies; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiOwinNet48 +{ + public class DefaultDependencyResolver : IDependencyResolver + { + protected IServiceProvider ServiceProvider { get; set; } + + public DefaultDependencyResolver(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + public object GetService(Type serviceType) + { + return this.ServiceProvider.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return this.ServiceProvider.GetServices(serviceType); + } + + public IDependencyScope BeginScope() + { + return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); + } + + public void Dispose() + { + // you can implement this interface just when you use .net core 2.0 + // this.ServiceProvider.Dispose(); + ((ServiceProvider) this.ServiceProvider).Dispose(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs new file mode 100644 index 00000000..0bb16012 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/DependencyInjectionConfig.cs @@ -0,0 +1,34 @@ +using System.Web.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiOwinNet48 +{ + public class DependencyInjectionConfig + { + public static void Register(HttpConfiguration config) + { + var services = ConfigureServices(); + + var provider = services.BuildServiceProvider(); + + var resolver = new DefaultDependencyResolver(provider); + config.DependencyResolver = resolver; + } + + /// + /// 使用 MS DI 註冊 + /// + /// + private static ServiceCollection ConfigureServices() + { + var services = new ServiceCollection(); + + //使用 Microsoft.Extensions.DependencyInjection 註冊 + services.AddControllersAsServices(typeof(DependencyInjectionConfig).Assembly.GetExportedTypes()); + + services.AddScoped(); + + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs new file mode 100644 index 00000000..ffb8667b --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/ServiceProviderExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http.Controllers; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiOwinNet48 +{ + public static class ServiceProviderExtensions + { + public static IServiceCollection AddControllersAsServices(this IServiceCollection services, + IEnumerable controllerTypes) + { + var filter = controllerTypes.Where(t => !t.IsAbstract + && !t.IsGenericTypeDefinition) + .Where(t => typeof(IHttpController).IsAssignableFrom(t) + || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)); + + foreach (var type in filter) + { + services.AddTransient(type); + } + + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs new file mode 100644 index 00000000..3a04e4e2 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/App_Start/WebApiConfig.cs @@ -0,0 +1,23 @@ +using System.Web.Http; + +namespace WebApiOwinNet48 +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Web API configuration and services + // config.Filters.Add(new LogFilterAttribute()); + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + + } + } +} diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs b/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs new file mode 100644 index 00000000..7a1b5afa --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/Commander.cs @@ -0,0 +1,15 @@ +using System; + +namespace WebApiOwinNet48 +{ + public class Commander + { + public Guid Id { get; set; } = Guid.NewGuid(); + public string Get() + { + var msg = "GG"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs b/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs new file mode 100644 index 00000000..99e3277a --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/Controllers/DefaultController.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Web.Http; + +namespace WebApiOwinNet48.Controllers +{ + public class DefaultController : ApiController + { + private Commander _cmder; + + public DefaultController(Commander cmder) + { + this._cmder = cmder; + } + + // GET + public async Task Get() + { + // this._cmder.Get(); + Console.WriteLine($"我在 DefaultController.Get ,Command.Id = {this._cmder.Id}"); + return this.Ok(this._cmder); + } + + private class Member + { + public Guid Id { get; set; } + + public int Age { get; set; } + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs b/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs new file mode 100644 index 00000000..3811e5f3 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/LabMiddleware.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Microsoft.Owin; +using AppFunc = System.Func, System.Threading.Tasks.Task>; + +namespace WebApiOwinNet48 +{ + public class LabMiddleware1 : OwinMiddleware + { + private readonly Commander _cmder; + + public LabMiddleware1(OwinMiddleware next, Commander cmder) : base(next) + { + this._cmder = this._cmder; + } + + // public LabMiddleware(OwinMiddleware next) : base(next) + // { + // } + + public override Task Invoke(IOwinContext context) + { + Console.WriteLine("我在 LabMiddleware.Invoke"); + + // Console.WriteLine($"我在 LabMiddleware.Invoke ,Command.Id = {this._cmder.Id}"); + return this.Next.Invoke(context); + } + } + + + public class LabMiddleware + { + private readonly AppFunc _next; + + public LabMiddleware(AppFunc next,Commander cmder) + { + if (next == null) + { + throw new ArgumentNullException("next"); + } + + this._next = next; + } + + public async Task Invoke(IDictionary environment) + { + try + { + await this._next(environment); + } + catch (Exception ex) + { + var owinContext = new OwinContext(environment); + + this.ErrorHandle(ex, owinContext); + } + } + + private void ErrorHandle(Exception ex, IOwinContext context) + { + context.Response.StatusCode = (int) HttpStatusCode.InternalServerError; + context.Response.ReasonPhrase = "Internal Server Error"; + context.Response.ContentType = "application/json"; + context.Response.Write(ex.Message); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Program.cs b/DI/Lab.MsDI/WebApiOwinNet48/Program.cs new file mode 100644 index 00000000..c2b4659a --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/Program.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Owin.Hosting; + +namespace WebApiOwinNet48 +{ + internal class Program + { + private static void Main(string[] args) + { + using (WebApp.Start(ServerSetting.HostEndpoint)) + { + Console.WriteLine($"伺服器已啟動, 位置:{ServerSetting.HostEndpoint}"); + Console.WriteLine("按下任意建離開應用程式"); + Console.ReadLine(); + } + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8a1889ce --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebApiOwinNet48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WebApiOwinNet48")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("680ec3cf-cfe2-48f9-8006-f6a3d9b5dc42")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs b/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs new file mode 100644 index 00000000..b76ef16c --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/Startup.cs @@ -0,0 +1,19 @@ +using System.Web.Http; +using Owin; + +namespace WebApiOwinNet48 +{ + public class Startup + { + public void Configuration(IAppBuilder app) + { + var httpConfig = new HttpConfiguration(); + DependencyInjectionConfig.Register(httpConfig); + WebApiConfig.Register(httpConfig); + //app.UseErrorPage(); + //app.UseWelcomePage("/Welcome"); + // app.Use(); + app.UseWebApi(httpConfig); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj b/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj new file mode 100644 index 00000000..dbd189c3 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/WebApiOwinNet48.csproj @@ -0,0 +1,111 @@ + + + + + Debug + AnyCPU + {680EC3CF-CFE2-48F9-8006-F6A3D9B5DC42} + Exe + WebApiOwinNet48 + WebApiOwinNet48 + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + True + + + ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll + True + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + True + + + ..\packages\Microsoft.Owin.4.1.1\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Diagnostics.4.1.1\lib\net45\Microsoft.Owin.Diagnostics.dll + + + ..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + + ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll + + + + ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + True + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.7\lib\net45\System.Web.Http.Owin.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WebApiOwinNet48/packages.config b/DI/Lab.MsDI/WebApiOwinNet48/packages.config new file mode 100644 index 00000000..4d964e03 --- /dev/null +++ b/DI/Lab.MsDI/WebApiOwinNet48/packages.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/App.config b/DI/Lab.MsDI/WinFormNet48/App.config new file mode 100644 index 00000000..1a57e048 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/App.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs b/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs new file mode 100644 index 00000000..00209ab0 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/DependencyInjectionConfig.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace WinFormNet48 +{ + internal class DependencyInjectionConfig + { + public static IServiceProvider ServiceProvider { get; set; } + + public static IServiceProvider Register() + { + var services = new ServiceCollection(); + ConfigureServices(services); + ServiceProvider = services.BuildServiceProvider(); + return ServiceProvider; + } + + /// + /// 使用 MS DI 註冊 + /// + private static IServiceCollection ConfigureServices(IServiceCollection services) + { + return services.AddSingleton() + .AddTransient() + .AddTransient() + .AddSingleton() + .AddScoped() + + //.AddTransient(provider => + // { + // var operation = provider.GetRequiredService(); + // return new Worker(operation); + // }) + //.AddTransient(provider => + // { + // var operation = provider.GetRequiredService(); + // return new Workflow(operation); + // }) + + //.AddTransient() + //.AddTransient() + //.AddLogging(loggingBuilder => + // { + // // configure Logging with NLog + // loggingBuilder.ClearProviders(); + // loggingBuilder.SetMinimumLevel(LogLevel.Trace); + // loggingBuilder.AddNLog(config); + // }) + ; + + ; + } + + //private static IConfiguration CreateConfig() + //{ + // var config = new ConfigurationBuilder() + // .SetBasePath(System.IO.Directory + // .GetCurrentDirectory()) //From NuGet Package Microsoft.Extensions.Configuration.Json + // .AddJsonFile("appsettings.json", true, true) + // .Build(); + // return config; + //} + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs new file mode 100644 index 00000000..abde6f86 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Form1.Designer.cs @@ -0,0 +1,61 @@ +namespace WinFormNet48 +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(47, 64); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.button1); + this.Name = "Form1"; + this.Text = "Form1"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button button1; + } +} + diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.cs b/DI/Lab.MsDI/WinFormNet48/Form1.cs new file mode 100644 index 00000000..3c3e1406 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Form1.cs @@ -0,0 +1,21 @@ +using System; +using System.Windows.Forms; +using Microsoft.Extensions.DependencyInjection; + +namespace WinFormNet48 +{ + public partial class Form1 : Form + { + public Form1() + { + this.InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + var serviceProvider = DependencyInjectionConfig.ServiceProvider; + var work = serviceProvider.GetRequiredService(); + Console.WriteLine(work.Get()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Form1.resx b/DI/Lab.MsDI/WinFormNet48/Form1.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs new file mode 100644 index 00000000..741af8b8 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/IMessager.cs @@ -0,0 +1,7 @@ +namespace WinFormNet48 +{ + public interface IMessager + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs new file mode 100644 index 00000000..0241069d --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace WinFormNet48 +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs new file mode 100644 index 00000000..76e8fce5 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace WinFormNet48 +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs new file mode 100644 index 00000000..0c0d795b --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace WinFormNet48 +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs new file mode 100644 index 00000000..7e18917a --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/LogMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WinFormNet48 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs new file mode 100644 index 00000000..2d0c9de3 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/MachineMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WinFormNet48 +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs b/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs new file mode 100644 index 00000000..878b27bf --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Message/MultiMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WinFormNet48 +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Program.cs b/DI/Lab.MsDI/WinFormNet48/Program.cs new file mode 100644 index 00000000..9f3dda6e --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Windows.Forms; +using Microsoft.Extensions.DependencyInjection; + +namespace WinFormNet48 +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + var serviceProvider = DependencyInjectionConfig.Register() as ServiceProvider; + + using (serviceProvider) + { + var form = serviceProvider.GetService(typeof(Form1)) as Form; + Application.Run(form); + } + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b7aebc8e --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WinFormNet48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WinFormNet48")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fc5aa11a-0879-43d8-8e79-dfc37de75fb8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs new file mode 100644 index 00000000..ca98cf3f --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinFormNet48.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinFormNet48.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs new file mode 100644 index 00000000..8411c43f --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinFormNet48.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings new file mode 100644 index 00000000..39645652 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj new file mode 100644 index 00000000..34b52830 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj @@ -0,0 +1,113 @@ + + + + + Debug + AnyCPU + {FC5AA11A-0879-43D8-8E79-DFC37DE75FB8} + Exe + WinFormNet48 + WinFormNet48 + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + + + + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings new file mode 100644 index 00000000..8a5228f0 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/WinFormNet48.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Worker.cs b/DI/Lab.MsDI/WinFormNet48/Worker.cs new file mode 100644 index 00000000..d6bcf3f2 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Worker.cs @@ -0,0 +1,25 @@ +namespace WinFormNet48 +{ + public class Worker + { + private IMessager Transient { get; } + + private IMessager Scope { get; } + + private IMessager Single { get; } + + public Worker(ITransientMessager transient, IScopeMessager scope, ISingleMessager single) + { + this.Transient = transient; + this.Scope = scope; + this.Single = single; + } + + public string Get() + { + return $"transient:{this.Transient.OperationId}\r\n" + + $"scope:{this.Scope.OperationId}\r\n" + + $"single:{this.Single.OperationId}"; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDI/WinFormNet48/Workflow.cs b/DI/Lab.MsDI/WinFormNet48/Workflow.cs new file mode 100644 index 00000000..8298237c --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/Workflow.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WinFormNet48 +{ + public class Workflow + { + public Workflow(IMessager operation) + { + Console.WriteLine(operation.OperationId); + } + } +} diff --git a/DI/Lab.MsDI/WinFormNet48/packages.config b/DI/Lab.MsDI/WinFormNet48/packages.config new file mode 100644 index 00000000..a2075034 --- /dev/null +++ b/DI/Lab.MsDI/WinFormNet48/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Client/Client.csproj b/DI/Lab.MultipleImpl/Client/Client.csproj new file mode 100644 index 00000000..e7e23018 --- /dev/null +++ b/DI/Lab.MultipleImpl/Client/Client.csproj @@ -0,0 +1,23 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MultipleImpl/Client/UnitTest1.cs b/DI/Lab.MultipleImpl/Client/UnitTest1.cs new file mode 100644 index 00000000..0dcc77b8 --- /dev/null +++ b/DI/Lab.MultipleImpl/Client/UnitTest1.cs @@ -0,0 +1,128 @@ +using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Server; +using Server.Controllers; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace Client +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void Autofac注入ServiceName() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureServices(services => { services.AddAutofac(); }) + .UseStartup() + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "autofac"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void Unity注入ServiceName() + { + var unityContainer = new UnityContainer(); + ConfigureContainer(unityContainer); + + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + .UseUnityServiceProvider(unityContainer) + .ConfigureServices(UseUnityController) + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "unity"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void 注入FuncName() + { + using var server = + new TestServer(WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(UseFuncName) + ) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "default/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + private static void ConfigureContainer(ContainerBuilder builder) + { + // builder.RegisterType().Keyed("file"); + // builder.RegisterType().Keyed("zip"); + // builder.RegisterType().WithAttributeFiltering(); + } + + private static void ConfigureContainer(IUnityContainer container) + { + container.RegisterType("zip"); + container.RegisterType("file"); + } + + private static void UseFuncName(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton>(provider => + key => + { + switch (key) + { + case "zip": + return provider + .GetService(); + case "file": + return provider + .GetService(); + default: + throw new NotSupportedException(); + } + }); + } + + private static void UseUnityController(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln new file mode 100644 index 00000000..8a158dd3 --- /dev/null +++ b/DI/Lab.MultipleImpl/Lab.MultipleImpl.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{DAE7F74D-E847-4B2F-8930-59AF2698FD1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NET5.TestProject", "NET5.TestProject\NET5.TestProject.csproj", "{A433C8F8-3B75-412E-955F-287639C55C5F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAE7F74D-E847-4B2F-8930-59AF2698FD1D}.Release|Any CPU.Build.0 = Release|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6ADCD6A4-6733-46CF-8935-C2D77FC7FC79}.Release|Any CPU.Build.0 = Release|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A433C8F8-3B75-412E-955F-287639C55C5F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs new file mode 100644 index 00000000..a1721c5a --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/AutofacStartup.cs @@ -0,0 +1,53 @@ +using Autofac; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NET5.TestProject.Controllers; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class AutofacStartup + { + public AutofacStartup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureContainer(ContainerBuilder builder) + { + builder.RegisterType().Keyed("file"); + builder.RegisterType().Keyed("zip"); + builder.RegisterType().WithAttributeFiltering();//<-- add line + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs new file mode 100644 index 00000000..50bdf475 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/AutofacController.cs @@ -0,0 +1,38 @@ +using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AutofacController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + public AutofacController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); + } + + [HttpGet] + [Route("{key}")] + public IActionResult Get(string key) + { + var serviceProvider = this.HttpContext.RequestServices; + var autofacServiceProvider = (AutofacServiceProvider) serviceProvider; + var fileProvider = autofacServiceProvider.LifetimeScope.ResolveKeyed(key); + return this.Ok(fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs new file mode 100644 index 00000000..fad0093e --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/DefaultController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private readonly ILogger _logger; + private readonly IFileProvider _fileProvider; + + public DefaultController(ILogger logger, + IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + [HttpGet] + public IActionResult Get() + { + // var fileProvider = this.HttpContext.RequestServices.GetService(); + var fileProvider = this._fileProvider; + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs new file mode 100644 index 00000000..43728f63 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/FuncController.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class FuncController : ControllerBase + { + private readonly IFileProvider _fileProvider; + private readonly ILogger _logger; + + public FuncController(ILogger logger, + Func pool) + { + this._fileProvider = pool("zip"); + this._logger = logger; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); + } + + [HttpGet] + [Route("{type}")] + public IActionResult Get(string type) + { + var fileProvider = this.HttpContext.RequestServices.GetService(type); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs new file mode 100644 index 00000000..48942f3f --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/MultiController.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class MultiController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public MultiController(ILogger logger, + // IEnumerable pool) + // { + // this._logger = logger; + // this._fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); + // var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + // Console.WriteLine(msg); + // } + // + // [HttpGet] + // public IActionResult Get() + // { + // var serviceProvider = this.HttpContext.RequestServices; + // var pool = serviceProvider.GetServices(); + // var fileProvider = pool.FirstOrDefault(p => p.GetType().Name == "ZipFileProvider"); + // return this.Ok(fileProvider.Print()); + // } + + public MultiController(ILogger logger, + Dictionary pool) + { + this._logger = logger; + this._fileProvider = pool["zip"]; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); + } + + [HttpGet] + [Route("{key}")] + public IActionResult Get(string key) + { + var serviceProvider = this.HttpContext.RequestServices; + var pool = serviceProvider.GetService>(); + var fileProvider = pool[key]; + return this.Ok(fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs new file mode 100644 index 00000000..c94576a0 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Controllers/UnityController.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using NET5.TestProject.File; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace NET5.TestProject.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnityController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + public UnityController(ILogger logger, + [Dependency("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + var msg = $"{this._fileProvider.Print()} in {this.GetType().Name} constructor"; + Console.WriteLine(msg); + } + + [HttpGet] + [Route("{key}")] + public IActionResult Get(string key) + { + var serviceProvider = this.HttpContext.RequestServices; + var unityServiceProvider = (ServiceProvider) serviceProvider; + var unityContainer = (UnityContainer) unityServiceProvider; + var fileProvider = unityContainer.Resolve(key); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs new file mode 100644 index 00000000..f9ea7bea --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/FileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace NET5.TestProject.File +{ + public class FileProvider : IFileProvider + { + public string Print() + { + var msg = "FileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs new file mode 100644 index 00000000..e72e465a --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/IFileProvider.cs @@ -0,0 +1,7 @@ +namespace NET5.TestProject.File +{ + public interface IFileProvider + { + string Print(); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs new file mode 100644 index 00000000..058d383d --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/File/ZipFileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace NET5.TestProject.File +{ + public class ZipFileProvider : IFileProvider + { + public string Print() + { + var msg = "ZipFileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs new file mode 100644 index 00000000..41034a00 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/FileAdapter.cs @@ -0,0 +1,19 @@ +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class FileAdapter + { + private readonly IFileProvider _fileProvider; + + public FileAdapter(IFileProvider fileProvider) + { + this._fileProvider = fileProvider; + } + + public string Get() + { + return this._fileProvider.Print(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs new file mode 100644 index 00000000..5be65458 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/FuncStartup.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public class FuncStartup + { + public FuncStartup(IConfiguration configuration) + { + this.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.AddControllers(); + UseFuncName(services); + } + private static void UseFuncName(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton>(provider => + key => + { + switch (key) + { + case "zip": + return provider + .GetService(); + case "file": + return provider + .GetService(); + default: + throw new NotSupportedException(); + } + }); + } + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj new file mode 100644 index 00000000..1c284244 --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/NET5.TestProject.csproj @@ -0,0 +1,28 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + + true + PreserveNewest + PreserveNewest + + + + diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs new file mode 100644 index 00000000..2c381a2f --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/ServiceProviderExtension.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using NET5.TestProject.File; + +namespace NET5.TestProject +{ + public static class ServiceProviderExtension + { + public static T GetService(this IServiceProvider provider, string name) + { + var pool = (Func) provider.GetService(typeof(Func)); + return (T) pool(name); + } + + public static List GetTypesAssignableFrom(this Assembly assembly) + { + return assembly.GetTypesAssignableFrom(typeof(T)); + } + + public static List GetTypesAssignableFrom(this Assembly assembly, Type compareType) + { + var results = new List(); + foreach (var type in assembly.DefinedTypes) + { + if (compareType.IsAssignableFrom(type) + && compareType != type + ) + { + results.Add(type); + } + } + + return results; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs b/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs new file mode 100644 index 00000000..2bad24fc --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/Startup.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace NET5.TestProject +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + this.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.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs new file mode 100644 index 00000000..6641e17a --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/UnitTest1.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NET5.TestProject.Controllers; +using NET5.TestProject.File; +using Unity; +using Unity.Microsoft.DependencyInjection; + +namespace NET5.TestProject +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void Autofac注入ServiceName() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + + // .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .UseStartup() //<-- add line + .ConfigureServices(services => + { + services.AddAutofac(); + services.AddControllers() + .AddControllersAsServices(); //<-- add line + }) + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "autofac/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void Unity注入ServiceName() + { + var unityContainer = new UnityContainer(); + unityContainer.RegisterType("zip"); + unityContainer.RegisterType("file"); //<-- add line + + var builder = WebHost.CreateDefaultBuilder() + .UseStartup() + .UseUnityServiceProvider(unityContainer) //<-- add line + .ConfigureServices(s => + { + s.AddControllers() + .AddControllersAsServices(); //<-- add line + }) + ; + using var server = new TestServer(builder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "unity/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void 手動註冊() + { + var hostBuilder = + WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(s => + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(p => + { + var fileProvider = p.GetService(); + var logger = + p.GetService>(); + return new DefaultController(logger, fileProvider); + }); + s.AddControllers().AddControllersAsServices(); + }) + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "default"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void 注入FuncName() + { + var builder = WebHost.CreateDefaultBuilder() + .UseStartup() + .ConfigureServices(s => + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton>(p => + key => + { + switch (key) + { + case "zip": + return p + .GetService(); + case "file": + return p + .GetService(); + default: + throw new NotSupportedException(); + } + }); + }) + ; + using var server = new TestServer(builder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "func/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + [TestMethod] + public void 注入相同的介面() + { + var hostBuilder = WebHost.CreateDefaultBuilder() + .UseStartup() //<-- add line + .ConfigureServices(service => + { + ScanToDictionary(service); + + // AddToDictionary(service); + }) + ; + using var server = new TestServer(hostBuilder) + { + BaseAddress = new Uri("http://localhost:9527") + }; + + var client = server.CreateClient(); + var url = "multi/zip"; + var response = client.GetAsync(url).Result; + response.EnsureSuccessStatusCode(); + + var result = response.Content.ReadAsStringAsync().Result; + Assert.AreEqual("ZipFileProvider", result); + } + + private static void AddToDictionary(IServiceCollection s) + { + s.AddSingleton(); + s.AddSingleton(); + s.AddSingleton(p => + { + var pool = + new Dictionary + { + {"zip", p.GetService()}, + {"file", p.GetService()} + }; + + return pool; + }); + } + + private static void ScanToDictionary(IServiceCollection services) + { + var assembly = Assembly.GetExecutingAssembly(); + assembly.GetTypesAssignableFrom() + .ForEach(t => { services.AddSingleton(t); }); + services.AddSingleton(p => + { + var pool = + new Dictionary + { + {"zip", p.GetService()}, + {"file", p.GetService()} + }; + + return pool; + }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json b/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lab.MultipleImpl/NET5.TestProject/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/DI/Lab.MultipleImpl/Server/AutofacStartup.cs b/DI/Lab.MultipleImpl/Server/AutofacStartup.cs new file mode 100644 index 00000000..1e93de1e --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/AutofacStartup.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Autofac; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using Server.Controllers; + +namespace Server +{ + public class AutofacStartup + { + public AutofacStartup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureContainer(ContainerBuilder builder) + { + builder.RegisterType().Keyed("file"); + builder.RegisterType().Keyed("zip"); + builder.RegisterType().WithAttributeFiltering(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices(); + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs b/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs new file mode 100644 index 00000000..45debe20 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/AutofacController.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AutofacController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + // public AutofacDefaultController(ILogger logger) + // { + // this._logger = logger; + // } + + public AutofacController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + this._fileProvider.Print(); + } + + [HttpGet] + public IActionResult Get() + { + return this.Ok(this._fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs b/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs new file mode 100644 index 00000000..79efc330 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/DefaultController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + + private readonly ILogger _logger; + + public DefaultController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + [Route("{type}")] + public IActionResult Get(string type) + { + var fileProvider = this.HttpContext.RequestServices.GetService(type); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs b/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs new file mode 100644 index 00000000..44910a17 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/UnityController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Unity; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnityController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + public UnityController(ILogger logger, + [Dependency("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + + [HttpGet] + public IActionResult Get() + { + var serviceProvider = this.HttpContext.RequestServices; + var unityServiceProvider = (Unity.Microsoft.DependencyInjection.ServiceProvider) serviceProvider; + var unityContainer = (UnityContainer) unityServiceProvider; + var fileProvider = unityContainer.Resolve("zip"); + var result = fileProvider.Print(); + return this.Ok(result); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs b/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..69c4d675 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Controllers/WeatherForecastController.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/DependencyConfig.cs b/DI/Lab.MultipleImpl/Server/DependencyConfig.cs new file mode 100644 index 00000000..b6df11a4 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/DependencyConfig.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +namespace Server +{ + public class DependencyConfig + { + public static void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddControllersAsServices() + ; + } + public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/FileProvider.cs b/DI/Lab.MultipleImpl/Server/File/FileProvider.cs new file mode 100644 index 00000000..06045138 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/FileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public class FileProvider : IFileProvider + { + public string Print() + { + var msg = "FileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs new file mode 100644 index 00000000..7c69a1ae --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/IFileProvider.cs @@ -0,0 +1,7 @@ +namespace Server +{ + public interface IFileProvider + { + string Print(); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs b/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs new file mode 100644 index 00000000..804a286a --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/File/ZipFileProvider.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public class ZipFileProvider : IFileProvider + { + public string Print() + { + var msg = "ZipFileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Program.cs b/DI/Lab.MultipleImpl/Server/Program.cs new file mode 100644 index 00000000..c083a4aa --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Server +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json b/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json new file mode 100644 index 00000000..f822a28b --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59369", + "sslPort": 44389 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Server": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj b/DI/Lab.MultipleImpl/Server/Server.csproj new file mode 100644 index 00000000..153ed02c --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Server.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MultipleImpl/Server/Server.csproj.DotSettings b/DI/Lab.MultipleImpl/Server/Server.csproj.DotSettings new file mode 100644 index 00000000..a9923e32 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Server.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs b/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs new file mode 100644 index 00000000..601138e2 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/ServiceProviderExtension.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public static class ServiceProviderExtension + { + public static T GetService(this IServiceProvider provider, string name) + { + var pool = (Func) provider.GetService(typeof(Func)); + return (T) pool(name); + } + } + +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/Startup.cs b/DI/Lab.MultipleImpl/Server/Startup.cs new file mode 100644 index 00000000..e7ecb450 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/Startup.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; + +namespace Server +{ + 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.AddControllers(); + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Server", Version = "v1"}); }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/WeatherForecast.cs b/DI/Lab.MultipleImpl/Server/WeatherForecast.cs new file mode 100644 index 00000000..36e011e2 --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace Server +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MultipleImpl/Server/appsettings.Development.json b/DI/Lab.MultipleImpl/Server/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/DI/Lab.MultipleImpl/Server/appsettings.json b/DI/Lab.MultipleImpl/Server/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lab.MultipleImpl/Server/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/DI/Lib.MsDiForScrutor/Lib.MsDiForScrutor.sln b/DI/Lib.MsDiForScrutor/Lib.MsDiForScrutor.sln new file mode 100644 index 00000000..d7ce1c92 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/Lib.MsDiForScrutor.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNetCore31", "WebApiNetCore31\WebApiNetCore31.csproj", "{7CBCB1D6-6FF5-44A0-A873-C0DCFD6630B8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7CBCB1D6-6FF5-44A0-A873-C0DCFD6630B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CBCB1D6-6FF5-44A0-A873-C0DCFD6630B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CBCB1D6-6FF5-44A0-A873-C0DCFD6630B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CBCB1D6-6FF5-44A0-A873-C0DCFD6630B8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D1F4D562-D1B5-4DFA-A971-5E7A1FA0E73F} + EndGlobalSection +EndGlobal diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/Default1Controller.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/Default1Controller.cs new file mode 100644 index 00000000..f639c8fe --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/Default1Controller.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class Default1Controller : ControllerBase + { + private IMessager Messager { get; } + + private readonly ILogger _logger; + //public Default1Controller(IMessager messager) + //{ + // this.Messager = messager; + //} + + public Default1Controller(ILogger logger, + IMessager messager + ) + { + this._logger = logger; + this.Messager = messager; + } + + + + [HttpGet] + public IActionResult Get() + { + var content = $"Messager:{this.Messager.OperationId}"; + this._logger.LogInformation("Messager:{message}", content); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/DefaultController.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/DefaultController.cs new file mode 100644 index 00000000..c6f658ba --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Controllers/DefaultController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private IMessager Transient { get; } + + private IMessager Scope { get; } + + private IMessager Single { get; } + + private readonly ILogger _logger; + + public DefaultController(ILogger logger, + ITransientMessager transient, + IScopeMessager scope, + ISingleMessager single) + { + this._logger = logger; + + this.Transient = transient; + this.Scope = scope; + this.Single = single; + } + + [HttpGet] + public IActionResult Get() + { + var content = $"transient:{this.Transient.OperationId}\r\n" + + $"scope:{this.Scope.OperationId}\r\n" + + $"single:{this.Single.OperationId}"; + this._logger.LogInformation("transient = {transient},scope = {scope},single = {single}", + this.Transient.OperationId, + this.Scope.OperationId, + this.Single.OperationId); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IMessager.cs new file mode 100644 index 00000000..07685fa7 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IMessager.cs @@ -0,0 +1,7 @@ +namespace WebApiNetCore31 +{ + public interface IMessager + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IScopeMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IScopeMessager.cs new file mode 100644 index 00000000..101c08fa --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ISingleMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ISingleMessager.cs new file mode 100644 index 00000000..e803b8c9 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ITransientMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ITransientMessager.cs new file mode 100644 index 00000000..b152a276 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/LogMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/LogMessager.cs new file mode 100644 index 00000000..633adcba --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/LogMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MachineMessager.cs new file mode 100644 index 00000000..d3e4038f --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MachineMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/Messager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/Messager.cs new file mode 100644 index 00000000..921613a7 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/Messager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + public class Messager : IMessager + { + public string OperationId { get; } = $"訊息-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MultiMessager.cs new file mode 100644 index 00000000..e4eb78a9 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Message/MultiMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Program.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Program.cs new file mode 100644 index 00000000..aba702a0 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Properties/launchSettings.json b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Properties/launchSettings.json new file mode 100644 index 00000000..139d8acd --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51385", + "sslPort": 44396 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "default1", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApiNetCore31": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Startup.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Startup.cs new file mode 100644 index 00000000..71a2aa39 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Startup.cs @@ -0,0 +1,72 @@ +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace WebApiNetCore31 +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public void AutoConfigureServices(IServiceCollection services) + { + var assembly = Assembly.GetExecutingAssembly(); + + services.Scan(scan => scan.FromAssemblies(assembly) + .AddClasses(classes => classes.AssignableTo()) + .AsImplementedInterfaces() + .WithScopedLifetime() + ); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + //AutoConfigureServices(services); + this.CustomConfigureServices(services); + } + + public void CustomConfigureServices(IServiceCollection services) + { + var assembly = Assembly.GetExecutingAssembly(); + var filterTypes = from type in assembly.GetTypes() + where type.IsAbstract == false + where typeof(IMessager).IsAssignableFrom(type) + //where type.Name.EndsWith("Messsage") + select type; + + foreach (var type in filterTypes) + { + services.AddTransient( type); + } + } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/WeatherForecast.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/WeatherForecast.cs new file mode 100644 index 00000000..1bb9e27c --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace WebApiNetCore31 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/WebApiNetCore31.csproj b/DI/Lib.MsDiForScrutor/WebApiNetCore31/WebApiNetCore31.csproj new file mode 100644 index 00000000..71dcbf40 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/WebApiNetCore31.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/Worker.cs b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Worker.cs new file mode 100644 index 00000000..f069f7e4 --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/Worker.cs @@ -0,0 +1,22 @@ +namespace WebApiNetCore31 +{ + public class Worker + { + public IMessager Messager { get; set; } + + public Worker(IMessager messager) + { + this.Messager = messager; + } + } + + public class Worker2 + { + public IMessager Messager { get; set; } + + public Worker2(IMessager messager) + { + this.Messager = messager; + } + } +} \ No newline at end of file diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.Development.json b/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.json b/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/DI/Lib.MsDiForScrutor/WebApiNetCore31/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} From ef896a7358aaf3a5b1c1af0b7782f41ac72810f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E5=B0=8F=E7=AB=A0?= Date: Fri, 20 Aug 2021 00:03:01 +0800 Subject: [PATCH 118/267] add --- DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln | 31 +++ DI/Lab.MsDIForAutofac/TestProject1/Hello.cs | 23 +++ .../TestProject1/HelloConsumer.cs | 24 +++ .../TestProject1/TestProject1.csproj | 17 ++ .../TestProject1/UnitTest1.cs | 25 +++ .../UnitTestProject1/App.config | 30 +++ .../UnitTestProject1/EntityModel/IDENTITY.cs | 39 ++++ .../UnitTestProject1/EntityModel/MEMBER.cs | 39 ++++ .../UnitTestProject1/EntityModel/MemberDb.cs | 24 +++ .../Properties/AssemblyInfo.cs | 20 ++ .../UnitTestProject1/UnitTest1.cs | 21 ++ .../UnitTestProject1/UnitTestProject1.csproj | 88 +++++++++ .../UnitTestProject1/packages.config | 7 + .../App_Start/DefaultDependencyResolver.cs | 39 ++++ .../App_Start/DependencyInjectionConfig.cs | 67 +++++++ .../App_Start/ServiceProviderExtensions.cs | 20 ++ .../WebApiNet48/App_Start/WebApiConfig.cs | 25 +++ .../Controllers/DefaultController.cs | 36 ++++ DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax | 1 + .../WebApiNet48/Global.asax.cs | 17 ++ .../WebApiNet48/Message/IMessager.cs | 7 + .../WebApiNet48/Message/LogMessager.cs | 9 + .../WebApiNet48/Properties/AssemblyInfo.cs | 35 ++++ .../ServiceCollectionExtensions.cs | 26 +++ .../WebApiNet48/Web.Debug.config | 30 +++ .../WebApiNet48/Web.Release.config | 31 +++ DI/Lab.MsDIForAutofac/WebApiNet48/Web.config | 58 ++++++ .../WebApiNet48/WebApiNet48.csproj | 184 ++++++++++++++++++ .../WebApiNet48.csproj.DotSettings | 2 + .../WebApiNet48/WebApiNet48.csproj.user | 45 +++++ ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 6044 bytes .../WebApiNet48/packages.config | 20 ++ .../Controllers/AutofacDefaultController.cs | 29 +++ .../Controllers/DefaultController.cs | 30 +++ .../Controllers/TestController.cs | 27 +++ .../WebApiNetCore31/Message/IMessager.cs | 7 + .../WebApiNetCore31/Message/IScopeMessager.cs | 6 + .../Message/ISingleMessager.cs | 6 + .../Message/ITransientMessager.cs | 6 + .../WebApiNetCore31/Message/LogMessager.cs | 9 + .../Message/MachineMessager.cs | 9 + .../WebApiNetCore31/Message/MultiMessager.cs | 9 + .../WebApiNetCore31/Program.cs | 22 +++ .../Properties/launchSettings.json | 30 +++ .../WebApiNetCore31/Startup.cs | 49 +++++ .../WebApiNetCore31/WebApiNetCore31.csproj | 20 ++ .../WebApiNetCore31.csproj.DotSettings | 2 + .../WebApiNetCore31.csproj.user | 6 + DI/Lab.MsDIForAutofac/WebApiNetCore31/a.cs | 29 +++ .../appsettings.Development.json | 9 + .../WebApiNetCore31/appsettings.json | 10 + DI/Lab.MsDIForAutofac/WebApiNetCore31/b.cs | 39 ++++ .../WebApiNetCore31.AssemblyInfo.cs | 23 +++ .../WebApiNetCore31.AssemblyInfoInputs.cache | 1 + ....GeneratedMSBuildEditorConfig.editorconfig | 3 + .../WebApiNetCore31.assets.cache | Bin 0 -> 358 bytes ...piNetCore31.csproj.AssemblyReference.cache | Bin 0 -> 174405 bytes .../WebApiNetCore31.csproj.nuget.dgspec.json | 70 +++++++ .../obj/WebApiNetCore31.csproj.nuget.g.props | 18 ++ .../WebApiNetCore31.csproj.nuget.g.targets | 6 + .../WebApiNetCore31/obj/project.assets.json | 88 +++++++++ .../WebApiNetCore31/obj/project.nuget.cache | 18 ++ DI/Lab.MsDIForAutofac/WinFormNet48/App.config | 6 + .../WinFormNet48/Form2.Designer.cs | 59 ++++++ DI/Lab.MsDIForAutofac/WinFormNet48/Form2.cs | 20 ++ DI/Lab.MsDIForAutofac/WinFormNet48/Form2.resx | 120 ++++++++++++ .../WinFormNet48/Form3.Designer.cs | 39 ++++ DI/Lab.MsDIForAutofac/WinFormNet48/Form3.cs | 20 ++ .../WinFormNet48/MainForm.Designer.cs | 95 +++++++++ .../WinFormNet48/MainForm.cs | 57 ++++++ .../WinFormNet48/MainForm.resx | 120 ++++++++++++ DI/Lab.MsDIForAutofac/WinFormNet48/Program.cs | 22 +++ .../WinFormNet48/Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Resources.Designer.cs | 71 +++++++ .../WinFormNet48/Properties/Resources.resx | 117 +++++++++++ .../Properties/Settings.Designer.cs | 30 +++ .../WinFormNet48/Properties/Settings.settings | 7 + .../WinFormNet48/WinFormNet48.csproj | 106 ++++++++++ .../WinFormNet48/packages.config | 5 + 79 files changed, 2551 insertions(+) create mode 100644 DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln create mode 100644 DI/Lab.MsDIForAutofac/TestProject1/Hello.cs create mode 100644 DI/Lab.MsDIForAutofac/TestProject1/HelloConsumer.cs create mode 100644 DI/Lab.MsDIForAutofac/TestProject1/TestProject1.csproj create mode 100644 DI/Lab.MsDIForAutofac/TestProject1/UnitTest1.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/App.config create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/IDENTITY.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MEMBER.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MemberDb.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTest1.cs create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTestProject1.csproj create mode 100644 DI/Lab.MsDIForAutofac/UnitTestProject1/packages.config create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/Web.config create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.user create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache create mode 100644 DI/Lab.MsDIForAutofac/WebApiNet48/packages.config create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/AutofacDefaultController.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/TestController.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.user create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/a.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/b.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfo.cs create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfoInputs.cache create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.GeneratedMSBuildEditorConfig.editorconfig create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.assets.cache create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.csproj.AssemblyReference.cache create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.dgspec.json create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.g.props create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.g.targets create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.assets.json create mode 100644 DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.nuget.cache create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/App.config create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Form2.Designer.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Form2.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Form2.resx create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Form3.Designer.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Form3.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.Designer.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.resx create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Program.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Properties/AssemblyInfo.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.Designer.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.resx create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.Designer.cs create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.settings create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/WinFormNet48.csproj create mode 100644 DI/Lab.MsDIForAutofac/WinFormNet48/packages.config diff --git a/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln b/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln new file mode 100644 index 00000000..b710049a --- /dev/null +++ b/DI/Lab.MsDIForAutofac/Lab.MsDIForAutofac.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNetCore31", "WebApiNetCore31\WebApiNetCore31.csproj", "{B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiNet48", "WebApiNet48\WebApiNet48.csproj", "{9EA9B67E-7812-41CB-899B-4331B5344882}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5E0DA79-326E-41CB-A95C-0C7FFDC70FF0}.Release|Any CPU.Build.0 = Release|Any CPU + {9EA9B67E-7812-41CB-899B-4331B5344882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9EA9B67E-7812-41CB-899B-4331B5344882}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9EA9B67E-7812-41CB-899B-4331B5344882}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9EA9B67E-7812-41CB-899B-4331B5344882}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {526A154E-E406-4F6B-A76D-4455CA7B02B1} + EndGlobalSection +EndGlobal diff --git a/DI/Lab.MsDIForAutofac/TestProject1/Hello.cs b/DI/Lab.MsDIForAutofac/TestProject1/Hello.cs new file mode 100644 index 00000000..6feacc21 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/TestProject1/Hello.cs @@ -0,0 +1,23 @@ +namespace TestProject1 +{ + public interface IHello + { + string SayHello(); + } + + public class EnglishHello : IHello + { + public string SayHello() + { + return "Hello"; + } + } + + public class FrenchHello : IHello + { + public string SayHello() + { + return "Bonjour"; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/TestProject1/HelloConsumer.cs b/DI/Lab.MsDIForAutofac/TestProject1/HelloConsumer.cs new file mode 100644 index 00000000..b20ad097 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/TestProject1/HelloConsumer.cs @@ -0,0 +1,24 @@ +using System; +using Autofac.Features.AttributeFilters; + +namespace TestProject1 +{ + public class HelloConsumer + { + private readonly IHello helloService; + + public HelloConsumer([KeyFilter("FR")] IHello helloService) + { + if (helloService == null) + { + throw new ArgumentNullException("helloService"); + } + this.helloService = helloService; + } + + public string SayHello() + { + return this.helloService.SayHello(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/TestProject1/TestProject1.csproj b/DI/Lab.MsDIForAutofac/TestProject1/TestProject1.csproj new file mode 100644 index 00000000..a9ae9b11 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/TestProject1/TestProject1.csproj @@ -0,0 +1,17 @@ + + + + net5.0 + + false + + + + + + + + + + + diff --git a/DI/Lab.MsDIForAutofac/TestProject1/UnitTest1.cs b/DI/Lab.MsDIForAutofac/TestProject1/UnitTest1.cs new file mode 100644 index 00000000..f5c735c4 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/TestProject1/UnitTest1.cs @@ -0,0 +1,25 @@ +using System; +using Autofac; +using Autofac.Features.AttributeFilters; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace TestProject1 +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + ContainerBuilder cb = new ContainerBuilder(); + + cb.RegisterType().Keyed("EN"); + cb.RegisterType().Keyed("FR"); + cb.RegisterType().WithAttributeFiltering(); + var container = cb.Build(); + + var consumer = container.Resolve(); + Console.WriteLine(consumer.SayHello()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/App.config b/DI/Lab.MsDIForAutofac/UnitTestProject1/App.config new file mode 100644 index 00000000..3002dd1f --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/App.config @@ -0,0 +1,30 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/IDENTITY.cs b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/IDENTITY.cs new file mode 100644 index 00000000..cd16740a --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/IDENTITY.cs @@ -0,0 +1,39 @@ +using LinqToDB.Mapping; + +namespace UnitTestProject1.EntityModel +{ + /// + /// + [Table("IDENTITY")] + public class Identity + { + /// + /// MEMBER_ID + /// + [Column] + [PrimaryKey] + public int MEMBER_ID { get; set; } + + /// + /// ACCOUNT + /// + [Column] + public string ACCOUNT { get; set; } + + /// + /// PASSWORD + /// + [Column] + public string PASSWORD { get; set; } + + /// + /// REMARK + /// + [Column] + [Nullable] + public string REMARK { get; set; } + + [Association(ThisKey = "MEMBER_ID", OtherKey = "ID", CanBeNull = false)] + public Member MEMBER { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MEMBER.cs b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MEMBER.cs new file mode 100644 index 00000000..1621a3eb --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MEMBER.cs @@ -0,0 +1,39 @@ +using LinqToDB.Mapping; + +namespace UnitTestProject1.EntityModel +{ + /// + /// + [Table("MEMBER")] + public class Member + { + /// + /// ID + /// + [PrimaryKey] + [Column] + public int ID { get; set; } + + /// + /// NAME + /// + [Column] + public string NAME { get; set; } + + /// + /// AGE + /// + [Column] + public int AGE { get; set; } + + /// + /// REMARK + /// + [Column] + [Nullable] + public string REMARK { get; set; } + + [Association(ThisKey = "ID", OtherKey = "MEMBER_ID", CanBeNull = true)] + public Identity IDENTITY { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MemberDb.cs b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MemberDb.cs new file mode 100644 index 00000000..6045d451 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/EntityModel/MemberDb.cs @@ -0,0 +1,24 @@ +using LinqToDB; +using LinqToDB.Data; + +namespace UnitTestProject1.EntityModel +{ + public class MemberDb : DataConnection + { + public MemberDb() + : base("MemberDb") + { + } + + //public ITable MJVNTRs { get { return this.GetTable(); } } + public ITable Members + { + get { return this.GetTable(); } + } + + public ITable Identities + { + get { return this.GetTable(); } + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/Properties/AssemblyInfo.cs b/DI/Lab.MsDIForAutofac/UnitTestProject1/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..80f817bf --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("UnitTestProject1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTestProject1")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("79bb1d0c-74b0-4b0f-acab-251831bfc96c")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTest1.cs b/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTest1.cs new file mode 100644 index 00000000..04a35b9f --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTest1.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using UnitTestProject1.EntityModel; + +namespace UnitTestProject1 +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + using (var db=new MemberDb()) + { + var members = db.Members.ToList(); + } + } + + } +} diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTestProject1.csproj b/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTestProject1.csproj new file mode 100644 index 00000000..0ba22660 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/UnitTestProject1.csproj @@ -0,0 +1,88 @@ + + + + + + Debug + AnyCPU + {79BB1D0C-74B0-4B0F-ACAB-251831BFC96C} + Library + Properties + UnitTestProject1 + UnitTestProject1 + v4.8 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + C:\Program Files (x86)\IBM\Client Access\IBM.Data.DB2.iSeries.dll + + + ..\packages\linq2db.2.6.0\lib\net46\linq2db.dll + + + ..\packages\linq2db4iSeries.2.6.0\lib\net45\LinqToDB.DataProvider.DB2iSeries.dll + + + + ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/UnitTestProject1/packages.config b/DI/Lab.MsDIForAutofac/UnitTestProject1/packages.config new file mode 100644 index 00000000..692b8154 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/UnitTestProject1/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs new file mode 100644 index 00000000..738882bb --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DefaultDependencyResolver.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Web.Http.Dependencies; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public class DefaultDependencyResolver : IDependencyResolver + { + protected IServiceProvider ServiceProvider { get; set; } + + public DefaultDependencyResolver(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + public object GetService(Type serviceType) + { + return this.ServiceProvider.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return this.ServiceProvider.GetServices(serviceType); + } + + public IDependencyScope BeginScope() + { + return new DefaultDependencyResolver(this.ServiceProvider.CreateScope().ServiceProvider); + } + + public void Dispose() + { + // you can implement this interface just when you use .net core 2.0 + // this.ServiceProvider.Dispose(); + ((ServiceProvider)this.ServiceProvider).Dispose(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs new file mode 100644 index 00000000..6e2d792a --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/DependencyInjectionConfig.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Web.Http; +using System.Web.Http.Controllers; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public class DependencyInjectionConfig + { + public static void Register(HttpConfiguration config) + { + var services = ConfigureServices(); + var builder = ConfigureContainerBuilder(services); + var provider = new AutofacServiceProvider(builder.Build()); + + //var provider = services.BuildServiceProvider(); + + var resolver = new DefaultDependencyResolver(provider); + config.DependencyResolver = resolver; + } + + /// + /// 使用 Autofac 註冊 + /// + /// + /// + private static ContainerBuilder ConfigureContainerBuilder(IServiceCollection services) + { + var builder = new ContainerBuilder(); + builder.Populate(services); + + var assembly = Assembly.GetExecutingAssembly(); + builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); + + return builder; + } + + /// + /// 使用 MS DI 註冊 + /// + /// + private static ServiceCollection ConfigureServices() + { + var services = new ServiceCollection(); + + //使用 Microsoft.Extensions.DependencyInjection 註冊 + services.AddControllersAsServices(typeof(DependencyInjectionConfig) + .Assembly + .GetExportedTypes() + .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition) + .Where(t => typeof(IHttpController).IsAssignableFrom(t) + || t.Name.EndsWith("Controller", + StringComparison.OrdinalIgnoreCase))); + + //services.AddScoped(); + + //services.AddTransient() + // .AddSingleton() + // .AddScoped(); + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs new file mode 100644 index 00000000..eda67822 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/ServiceProviderExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public static class ServiceProviderExtensions + { + public static IServiceCollection AddControllersAsServices(this IServiceCollection services, + IEnumerable controllerTypes) + { + foreach (var type in controllerTypes) + { + services.AddTransient(type); + } + + return services; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs new file mode 100644 index 00000000..8ee1c430 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/App_Start/WebApiConfig.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http; + +namespace WebApiNet48 +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + DependencyInjectionConfig.Register(config); + // Web API configuration and services + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs new file mode 100644 index 00000000..9f16bc34 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Controllers/DefaultController.cs @@ -0,0 +1,36 @@ +using System.Net.Http; +using System.Web.Http; + +namespace WebApiNet48.Controllers +{ + public class DefaultController : ApiController + { + private IMessager Messager { get; set; } + + public DefaultController(IMessager messager) + { + this.Messager = messager; + } + + [HttpGet] + public IHttpActionResult Get() + { + var content = $"Messager:{this.Messager.OperationId}"; + return this.Ok(content); + } + + [HttpGet] + public IHttpActionResult Get1() + { + var messager = InstanceManager.Messager; + + var content = $"Messager:{messager.OperationId}"; + return this.Ok(content); + } + } + + public class InstanceManager + { + public static IMessager Messager { get; set; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax new file mode 100644 index 00000000..7946eef2 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="WebApiNet48.WebApiApplication" Language="C#" %> diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs new file mode 100644 index 00000000..13555dcb --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Global.asax.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http; +using System.Web.Routing; + +namespace WebApiNet48 +{ + public class WebApiApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs new file mode 100644 index 00000000..8544b3b9 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/IMessager.cs @@ -0,0 +1,7 @@ +namespace WebApiNet48 +{ + public interface IMessager + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs new file mode 100644 index 00000000..55bac639 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Message/LogMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNet48 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..09e000e3 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebApiNet48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WebApiNet48")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9ea9b67e-7812-41cb-899b-4331b5344882")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs b/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..3fe60da6 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/ServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; + +namespace WebApiNet48 +{ + public static class ServiceCollectionExtensions + { + /// + /// Adds the to the service collection. ONLY FOR PRE-ASP.NET 3.0 HOSTING. THIS WON'T WORK + /// FOR ASP.NET CORE 3.0+ OR GENERIC HOSTING. + /// + /// The service collection to add the factory to. + /// Action on a that adds component registrations to the container. + /// The service collection. + public static IServiceCollection AddAutofac(this IServiceCollection services, Action configurationAction = null) + { + return services.AddSingleton>(new AutofacServiceProviderFactory(configurationAction)); + } + } + +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config new file mode 100644 index 00000000..c1a56423 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config new file mode 100644 index 00000000..19058ed3 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config new file mode 100644 index 00000000..2a9874d6 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/Web.config @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj new file mode 100644 index 00000000..dd49038d --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj @@ -0,0 +1,184 @@ + + + + + Debug + AnyCPU + + + 2.0 + {9EA9B67E-7812-41CB-899B-4331B5344882} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + WebApiNet48 + WebApiNet48 + v4.8 + true + + 44327 + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Autofac.6.0.0\lib\netstandard2.0\Autofac.dll + + + ..\packages\Autofac.Extensions.DependencyInjection.7.1.0\lib\netstandard2.0\Autofac.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + + ..\packages\Microsoft.Extensions.DependencyInjection.3.1.9\lib\net461\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.9\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Diagnostics.DiagnosticSource.4.7.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + + + + + + + + + + + + + Global.asax + + + + + + + Web.config + + + Web.config + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 54526 + / + https://localhost:44327/ + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings new file mode 100644 index 00000000..8a5228f0 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.user b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.user new file mode 100644 index 00000000..9749d8ec --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/WebApiNet48.csproj.user @@ -0,0 +1,45 @@ + + + + true + + 44327 + + + + + Debug|Any CPU + ApiControllerEmptyScaffolder + root/Controller + 600 + True + False + True + + False + + + + + + api/default + SpecificPage + True + False + False + False + + + + + + + + + True + False + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/DI/Lab.MsDIForAutofac/WebApiNet48/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000000000000000000000000000000000000..226d11bd723bf60a5be6c834823fbaf939b181b1 GIT binary patch literal 6044 zcmeHLZBrXJ5VpZ67($9^N(eNG+oo;O@!Z(uotDlJj8jho!`L43fn$j7Zfz8FCy%59 zT!!D$U(o4KY**(O2qC1Nv1j<;^W2`i(ymskmGo$1@mMVOFFxjLp8G>cylv)6p+r?% zdrXCtmv!hdVQmWE7eO(vfP;iOOP754C!X!jCEE z;uO3MrNDQ1W9v*qV60LT*m_e0RaPr74%03TJmygi?UshC=Bgp*1xbB474o=URiO^n zJ^Y}qAh?I)eQKB$Dr=xSV&%|Lpm$J-dd_NrkdTKeMOPF9rP01i>zayxy1h|TU9;&U zcC(5nAMvkBr8$mM>1$@@nFLo0$v{okJxyg9sC18ZsXkJZwQG#=x)fFPkQvpKgc?&? zHZBE{I4}*yg|n#Wsxz+iGJUOTTLkPF>K87dGud7YKS+5}g$z1CVI_q6-fB%*yxFYr_4`G)w`R?aV)3TN< zsAbZ=a)so~xm;rl|9gooFVjVLaVfVtzW~{l)q~7!cHB`@`{9R!-n#XUtBc16A=-~9 z>a3=ID8J(|)#C*;2+)4S&>%p2b#NZU4GvBYBNjo7F*1SV)p@CfoYu++q)({?y90J@YiFy{!H}7g1#2bK2B0mlxpj6y9-yA%{F<$# zYb)y4Iar?14i_~$U$oy)$J$o;Vgb=ms6~~!_FHf^>TCz}!ip`N_jVqf5Zi)6)LB7f zR01XVNe2dB!XV3~?hz!vFxF2Znf=hfHrh9ak`&fYnfi2R1Zl&cLVhA@@ueyGigEG~ zOJm5(TJzcY+-mYG{2v=j8NZsBOdDdIvcUJqBP=H@k8?5;mEa6g#`VVY#NhqqZd~!2 z3x{%xDtbm{QFy5vVf3TrM)wCYhqAwPBU`8LF|C1WEBG1}{MwBI(|I&;Z4FOQ!*AVc zC;WoH*EY2b*qBuNS?>EYX&5h}Sw_jT|cWCyXARH1iBt^G{-Mr}|8W<&)RH ze#CvRC%?xCbN^s;H2D;Fz4ntbhquW&k!MKCn`<`S`)io~Nk`eB^oz5VrI&W+NKCiR zW6YRbIk!6}-iIf+ZH_%VWspJCLmCT-0%nWi1|>w$;i@Td0~ z$;|oYjp?fk@*UPro&U}PZ4wqHlTpN_b3vXXfA+j`b8e<_LB7ZOUci$dP|w79iJlq< zn%vC=`4L&&9(EnX;;_I@83BNOn-s8)Wc{=MIZ+|xfjiOJF`0c T_h1A2T#9(Gfpt=M2jl+%2EfII literal 0 HcmV?d00001 diff --git a/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config b/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config new file mode 100644 index 00000000..7d6100d0 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNet48/packages.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/AutofacDefaultController.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/AutofacDefaultController.cs new file mode 100644 index 00000000..cb8c33b4 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/AutofacDefaultController.cs @@ -0,0 +1,29 @@ +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AutofacDefaultController : ControllerBase + { + private readonly IFileProvider _fileProvider; + + private readonly ILogger _logger; + + + public AutofacDefaultController(ILogger logger, + [KeyFilter("zip")] IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + + [HttpGet] + public IActionResult Get() + { + return this.Ok(this._fileProvider.Print()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs new file mode 100644 index 00000000..8402dcb9 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/DefaultController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DefaultController : ControllerBase + { + private IMessager Messager { get; } + + private readonly ILogger _logger; + + public DefaultController(ILogger logger, + IMessager messager + ) + { + this._logger = logger; + this.Messager = messager; + } + + [HttpGet] + public IActionResult Get() + { + var content = $"Messager:{this.Messager.OperationId}"; + this._logger.LogInformation("Messager:{message}", content); + return this.Ok(content); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/TestController.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/TestController.cs new file mode 100644 index 00000000..aafcda8e --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Controllers/TestController.cs @@ -0,0 +1,27 @@ +using Autofac.Features.AttributeFilters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace WebApiNetCore31.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TestController : ControllerBase + { + private readonly ILogger _logger; + private readonly ITestService _testService; + + public TestController(ILogger logger, + [KeyFilter("service")] ITestService testService) + { + this._logger = logger; + this._testService = testService; + } + + [HttpGet] + public IActionResult Get() + { + return this.Ok(this._testService.GetDate()); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs new file mode 100644 index 00000000..35d3c543 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IMessager.cs @@ -0,0 +1,7 @@ +namespace WebApiNetCore31 +{ + public interface IMessager + { + string OperationId { get; } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs new file mode 100644 index 00000000..c91f6890 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/IScopeMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface IScopeMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs new file mode 100644 index 00000000..5d2f7fb1 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ISingleMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ISingleMessager : IMessager + { + } +} diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs new file mode 100644 index 00000000..4238a3ee --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/ITransientMessager.cs @@ -0,0 +1,6 @@ +namespace WebApiNetCore31 +{ + public interface ITransientMessager : IMessager + { + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs new file mode 100644 index 00000000..f8725cea --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/LogMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class LogMessager : IMessager + { + public string OperationId { get; } = $"日誌-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs new file mode 100644 index 00000000..9feede22 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MachineMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + internal class MachineMessager : IMessager + { + public string OperationId { get; } = $"機器-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs new file mode 100644 index 00000000..673a9a2c --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Message/MultiMessager.cs @@ -0,0 +1,9 @@ +using System; + +namespace WebApiNetCore31 +{ + public class MultiMessager : IScopeMessager, ISingleMessager, ITransientMessager + { + public string OperationId { get; } = $"多個接口-{Guid.NewGuid()}"; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs new file mode 100644 index 00000000..3d6598b4 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Program.cs @@ -0,0 +1,22 @@ +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace WebApiNetCore31 +{ + public class Program + { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + //.ConfigureServices(services => services.AddAutofac()) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } + + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json new file mode 100644 index 00000000..b39adb37 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:54205", + "sslPort": 44308 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "default", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApiNetCore31": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs new file mode 100644 index 00000000..0cc35cfc --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/Startup.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using Autofac; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace WebApiNetCore31 +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + public void ConfigureContainer(ContainerBuilder builder) + { + var assembly = Assembly.GetExecutingAssembly(); + builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj new file mode 100644 index 00000000..93ae2103 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings new file mode 100644 index 00000000..8a5228f0 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.user b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.user new file mode 100644 index 00000000..dc63f8a8 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/WebApiNetCore31.csproj.user @@ -0,0 +1,6 @@ + + + + false + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/a.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/a.cs new file mode 100644 index 00000000..c765a72d --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/a.cs @@ -0,0 +1,29 @@ +using System; + +namespace WebApiNetCore31 +{ + public interface IFileProvider + { + string Print(); + } + + public class FileProvider : IFileProvider + { + public string Print() + { + var msg = "FileProvider"; + Console.WriteLine(msg); + return msg; + } + } + + public class ZipFileProvider : IFileProvider + { + public string Print() + { + var msg = "ZipFileProvider"; + Console.WriteLine(msg); + return msg; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json new file mode 100644 index 00000000..dba68eb1 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json new file mode 100644 index 00000000..81ff8777 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/b.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/b.cs new file mode 100644 index 00000000..6e1a86a2 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/b.cs @@ -0,0 +1,39 @@ +namespace WebApiNetCore31 +{ + public interface ITestService + { + string GetDate(); + } + + public class TestService : ITestService + { + public string GetDate() + { + return "service"; + } + } + + public class TestComponent : ITestService + { + public string GetDate() + { + return "component"; + } + } + + public interface IServiceProvider + { + public void setService(); + } + + // Client class + public class Client + { + private IServiceProvider _service; + + public Client(IServiceProvider service) + { + this._service = service; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfo.cs b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfo.cs new file mode 100644 index 00000000..1296b66c --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfo.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("WebApiNetCore31")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyProductAttribute("WebApiNetCore31")] +[assembly: System.Reflection.AssemblyTitleAttribute("WebApiNetCore31")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfoInputs.cache b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfoInputs.cache new file mode 100644 index 00000000..b94c1aa8 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +563fea439ee8548554f13f24a19fba2d41da1919 diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.GeneratedMSBuildEditorConfig.editorconfig b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.GeneratedMSBuildEditorConfig.editorconfig new file mode 100644 index 00000000..f362fefd --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.GeneratedMSBuildEditorConfig.editorconfig @@ -0,0 +1,3 @@ +is_global = true +build_property.RootNamespace = WebApiNetCore31 +build_property.ProjectDir = D:\src\sample.dotblog\DI\Lab.MsDIForAutofac\WebApiNetCore31\ diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.assets.cache b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.assets.cache new file mode 100644 index 0000000000000000000000000000000000000000..6440b2a6acff8db9af7cf6a8e8ef42d92a1f4b55 GIT binary patch literal 358 zcmaiuF-yZx5XV!iA`X6r8*Im5x~M^inidhU7E6R+mgmbQJsaLD?_O*R{R*z)+(BGi z{3=c^;^gY=W&(8+Jov+L{C~%G>;En-tJzH7Eh16&H3W$^ZWV3uSfS6 zf%lbeAJ)p!KT}njfggvAhT}+R2!=vs5EZejiVST$7IGvq T(DQb@jRD@GAv;^^8l%k*f>2}E literal 0 HcmV?d00001 diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.csproj.AssemblyReference.cache b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/Debug/netcoreapp3.1/WebApiNetCore31.csproj.AssemblyReference.cache new file mode 100644 index 0000000000000000000000000000000000000000..d5029fad62f2b044f81a2e82e585ea24cbbb99eb GIT binary patch literal 174405 zcmds=3!EH9weWW!JWP}T2@(Pc2?|#rncdCi9dfy0^OQW6Y)AqoK<`ZN?j$qQlb+t) zY$OSf0D=%85l|FZKU5GTKJhBP;1U(Spj=);R8V<*qVm(r54k9cepNm7>YnPJTAWk8 zmF1UY!&Xhs>H7DnQ>V_UT6WgT_SRHtY^tFlm2I89uFZA2T&pl)VJ>f%*JT~AXnX5Q zR%T;)-LhQ9b;?esmtMYLRjcFL>3OA6dZpdDt|i@^Zd&J}?@;Ru`iWI4q5miJXl?Jd z+)_H5&!^D8)X>nkurHH)_V83HbtM1TN~6>;+(7v9JD|U(wxi#MU4p(rf2Q)CNW$5T zjg76d8v{Z&R)lW!g>K}Z9um6I7rGG%-53yB=phI_fd0vb4_5y9PoQrPJ*e{cmF@GI z8<4;$^xt7myuD`cp8fxGXL`pyBlo=XzTdt2&W6{nzv^!X_vSBO`l(&xzc%N$x9)%D zmk&LE>yy(*O+4s^5$+j(z2v6tdw!GIA~Vq z$~{*8EUTQ$qE)>FTFf`E05R9VIpL8@Yl&|iYaX_H>2u=oR5BBO9RPx^#IB$;5R z*5ryUQ_^j2u8{L`y>^*TbD6t7S#77n&HV`v+a^4x;IG8&R9G@Y?bLb>R#+oW*Hq-Q zOsSWdvg>W8+WX6%UBC`jt5fvs&7O>@qpDi@C}E7^Q7S;0rz(LAC1_QZA|g0b>IY5- z5IAS`baoymoV$+r$6epM^Q8QS z>t+mJ`qaU1jb6O5_}!DvyW$Ho7mY8zx#_W8pR>+r_}1I@&rbO1ulK$3MyBb}ffMIl ze(oK||LKE|oqFt#_K$k$^YdC}k2_@l_n$m{=04xqe(a;8{`r&1&!3YzC-_W7Sen#e z$z*19G`DniX0lVV+3d7t{0PFX_9h@$TAh5}&Uk2PP)2@sG1KF^c2R0^#t}W`Js3(O zE7C_yWc|P>$%Ql25?QfNJP2BI-Q4by2GW)k3O%0Hk+->#IyQeBahFbzsLeX~Ap~r$ z;um(CHFCyeGZeE6ojLv zI72zRNWvLcii1{H@7&d3&Du+LCTHbyTkI?+?R)R}G_hynC3*{86j9rv(V_{`8OoY$ z>J+pBf~`{nY!Xv;bTwd#P!%;oWoDr2-0Eiw1WykbJcW`|L{9xOCu`@^^NK~svnmch zW9e6qUqX!4M5b?4FCVRg@d72*)87h?R+NB-nyhTiCoh8M?_kwu7pGqjc=IjKlET+; z)Kdh$ljV;f)YAu$u;>4QLG*tGHVH3H=_N2Er80&0|NVyF9<;sds_}iZuYcj{nU9Qn z`k)8wZ%-I^(Ru%V^WI(d_>X<&p)*Gx^N0J-{L)d)dqzCf^20w~G_h;)En|M)eD|L3 zy>H;K#*?4h`hfH3$al`$`Sj1Hy}WeRwF_Dgd-bI`J5Curch945-h8=p%Oh_sYJ26D zb=eok+_B(=KW{wk^rIKu_pp8Xn-3p;$oen((TwIF%v^on^ySl1-AY3slBl?!*wWn5 z*)nt5bSv93eX89utulbY{X|V9F6c$p5N$2m*?HyuqOdyn{p`=glNjo$ILqoLF(joj zi9>4>JA%ZJaw3U!4C;G&Y@%%6h%X!_$lpcY$>*-Z0|su_Z^q(*w)6kP2leR%@loh#)nkQEFFxmK5D!mJCHKx_VrH z{mM-jUqOs!S_%yrMueiuVgWB20vMIRZIvLCp%kr=J>MWib<5g;+8;-AU)d42o^E*V zQ^X9>{c+O72M61nWcSBKfd??dHTK7cmLE^|$B7mqEOm6)F9fqVDA$=ySd-6snvgPr zy$G(YXkWU(?F$k0NX!WNVlyXB5dP0C>sB8(_19OYmJ8C01ERQ=@0c;enwFV1y?Iv4 zRJ&>BtjfO?T+2hn!f}`dWhxX3PLYob_g!f$TxjMGPwiC_bSzwu$WY@?W8s1(B0P0m z2|;ZS`;z0|+HG&aX3s9-oOmHqigQG_hvhxk8_yh4^11~*3h3=&xo|#RwQT_1j+K9i zIIvB)#6!U`RImzAX4+RG#!Ta%vq2+c72yU#n`tI$?IEIO8g~f}h?J)^mZp*Ea#%2W$-Sb*dWsaMkwiJz%%3N-jge`bgAs7NM%6#}5@P4U7k~V(m_k zn-Nw5Pu$&43>b};#`R-%EIw-yI$9bhqB32UkT6B$pEymz5)48Qnkbel#?7m`q!zM&P{uxmg2AbfS3QtMuNE71g%R%h(8 z;I;7_?Xl6#eb{O*x4}wpqa`yGvh~p#iEx>){ASZ>Io&h3&SfiCVgScyjM|=(En9tk zfS`gZNC_AMJgtUTtURxb7NkUUQ3MRSASFUNL#-JtNQux22sRz71U#$eiqg=VY?VNo z`;ZN-Rf4F9d3H>;N+24EaMf{@P=IpYT&oWQ70M&^x7xT7JC3>sRjnCX@tbo!(9w=6 z!3;HB>mr|Y5G=J#LWN_+pb~%PlM!M2gy)x^2_^oJCO$&gm7!l4CI43-V+{IW3{l_# zEQ(Q{6;1phT8OZy=9wtKvH{8p^%t!IDji?hQ}l8LAxLriKL-dj@24+Du&ECZh9y30 z%!Rf;L8%NiKIh4wu?SNg;}KBK`J%kjfeXYdd8@5f-Y#Y>`4K(TIgjX~2p6<-9wD8f zkWuG6LMtHH%0QMYsD&)eiN{$_B(GOgA4S|g3T(@w#50t)jv7xL2so&${NU&??~2E= zXjXopp(0w)S@{8(4CRStDf(R;MY{?3o)mw7Wh9k+ z31iX@K2){C3eX8-QrUcEYA5~C1TE=jh=Z!Slvl_CgerFp>3}!=iRoijg0I17xoc!6 ze{k_Kht zpCkpnt1A~yact=>s{BKO1FLqJn!>Ger4`uCG2J~_>LOOp;2k>lQRA7uQHzTwX zVXI?o18TV$)It`tf6s!OiSI_2n^Aofaf4oNMu}%AZ*;jCr4|ryfGY*Tx~;LiOt+ZM z!)v%`X-QCT(m@e54J(BRWz4f{!pfyuCm_h6Vj4kaB(4vQX#^T7LIoYu2*_lp(V{Vp zfG$7~LG5HC*_WWB1}x9cwtJ3ibqT8??t;;s?CREvI6?1ZR|{q+Q*Y@YM9GW1k|fB{?uo?9$fMKs4{E2gR4Lw7+I{8!nJ;PZR? zgjJxlk0MYSR)JFS47ECSS&*=&iN5f83-t-P=G%g zZ5FyDH;>NGXGVw9L_-0AhCWItGL3Ht_P_i|5PcUsA_zJZ5Rl1Gj%X+#po<7m9Z#-= z+Dr3gKc~lb`-9hB@oZAMmlkZPh!ON&T2LrMIih=eL1lnIf;#ES7hYn7=z)az=Ze!9 z^hsAt)B$V&joPB4CvRaDL_q53|5!l&Pi6JR=P7<@!~4j5Txr1|VG|P`CG-`XpmM;z zjv(#;{?}_@GPF*gX$w5jASg=ZFe>NFK*e~L+g^IH=ao1rJa9;3JhTmiCBCKsI>tlA zF?0f=F&?T$5SY5%NDZ~JqGDGcp>Xo<&ygK|y0Qwl^#Q`~cRWDQE32?r${-CbyF*u2 zVRb~H>UjAuqy<`L<%C5Q2T8O*xQ!w}IxG+qc>n`#)PyBkAgl%uFi;z+nD^yY!Cq;j zZPgxpi5;K3_)3ays8+Z3A*!9u2ECzLEts+$8d~R{(GAsVg+#FGn8XH^!|W?ydwq_( z5ob!eh0qNX94?x}Ebrk1Ry*npox>~_&QQo`PPJSq5wtq4&LF3}w!m>2a;F=&QM6V% zraUIH%9J;>Y?L_VVKqcx>S&pu0*9D-SyC-8h@yeRU`s`KpaX|Np$xS^G;kPH1_-1s zptukgex<#s$1aNpv2%xe6p3Ys?Gzz$fMZ0WNQNS`TJm%a1gPE4^q{j8`~6Yy1BZP2 z5&@#^h7dqhqyqpTstOQ5kny8-+wIL|>^3J?^kfi;@guRV4-#H(Cm%l&#Zrc8=*Evk zbwr@*7(ar#3kuH*?M`N+EzW4%F*;hXxultoQSJ2^^j%P4!Oiok>0MBQCL%m_T!}%Y zbzsTY*d49;9J&phnemWh!EA$0zp;U64@8qR@Qb~ z9h4!I2}UJ(nFp=3jp(B2DM44-Mo6cO*w9-0(@NV2twh-B7z%(~!6A!^Hm+2+;Ct3f8mIf|0{4zZ>NUvBo=f4i%=sSU})K$W&Bx+aHt+Sm0*>} z@}7e2;>$%>Bahn=k5c6xH;W~O+A>RXC>}8McJx5vb<$ zbfp`F6@UWUn5E)NOb*LMB5f4y4*15bD01zC1=&U@=_U@4>Km_c8W~iuZ$-xj^r|m& zvhpGv8tfCAvG+VaW(*zd6C^V96{o>IK@$<4Iy$9L<;g4UP8yn-pDUN}nL(jtG@mwT zd2&=A_V&k5AJFB=QQ{fO7%fkZQcDD`seaQ5)Gfk-8Vd?JFTG^xB7EyyWLg=$ML4b> zvuDg-@x7O@-vWJ$aGZ#S!bfirj#EtpusN+%-O-`o&p_QG99Cs%uB+SYv+?i#Ao&dO zHTg+oB@ok15kKf#gkvN$lsgT6c^B$c6H z(aTdp+5};$TiFp*)LN8&GrGGKpARpb>cG#Hp;2pT2Su=;qt;T{48@B^t))5vK?ZfB zp`gPu+q1HkXYpy0=eW@u4JFM}ct#zDe#IYO4t?*rL^4D1qBj~!G~#eIr>CaX4pLX} z#WYwjSt>U_zDQ!hc!!~9!MJROA||n5Tqh3MROIg0ZJLC-YeRTVR`r)`*}YGvkGPm@ zqR_iGB0DL<27T8?ql%j{{ZtZkc}?smsaYD@8v2MRZa`41W5Hkk3%o`1BW{7Z7ytR%{KbiV|<3;KwCb zTd_)8Ma1-3F+m0MCd6thR#68Cs4Y^!+uWuuH|JZXxA%VOhXM@PHhlG8tb@;`BB|8uFjQvtbD6%YW=rbPlEJSG=G;T z#-pgCdG>IF;xLFr*3rv@Isj-Tu^zJM2ES&E9spF`+NZAe`V;y9plZPkwR`jcplXFg zu3PCU@f+a*}!EgJUGjCEQ3uI4H@{3Wl$S(2|NC8irONwYw z;FOpbM_v=@5mS71+8naowLgV9cXWp)Q|w73RQbeQ4AX!h95GQq7Ky@ z=~{Sj!IbG5TGiO$g$Gwi1gl9cPgms>cBpb$`g$yN@DUO+sA#!tu%!=yKVh zP|7e3tujbjE*n%v1gVY*-(Y(YWXZ%TBbj~TCq%Hlh)`PwQ+(Df_+CUvEJJZ(dl4aZ zfRyb(_>|qPnzqEM1vC`DY|SSh1g~xh zyn0O`PZjmY$#=wf@>{$qQhO!uM3b}eBk#$^;f@S*>-y&!;))s`j zRCuiwOLvOu#QeW_LkOPk6eXXb?6GvGD7{4JnhmI9km|wK8T^bMR>vUgz-C<%0jZqW zgIBVF%?j{JESYRRXtj^hV08ns&kzT-j^}E006SG|7%%(kF5=A}5uC>rXQ~aF+$AjV z0A|Rj%rkMB;1N1g|NGd}o7P?Iy z(nf@-j@#r=UNx8A?Js#wmur=}`_uFCT@Fg7?H1|c*|gNFuGQDa?O5CD;=cS7bd5`V1xFc)VrenRGb6=_Up zLgsQB%47V5%+&)T5-c4}ZamgbYns(+yB@lnK4W>Ph?Ff}`SeWF(bPV{$Fp`67CaqI zOA|Vx;AynO3(Lh3H2vrMaC~P!C zs9GTrtUCIKP^ZT*1^jR9cAS9LC9`-m>MHG^h!=F!RVtgIgwZIlR3{+FppL(0IZiY4<@kNf6 z^SIF+6WkziaA>+&xUC{U(CKDjv6Ml&|L-^a_Mq)uSB>wRefUr$A9cI51l#sm_OWq=9i9Y-ZSETF~xqBXc^XAK)TON69QQIrGtjoSQ=8gp~ z{CVSPrysrOzK8A8-+cJ+L)L#WbvSzSntw2J^?lQqPfO8sv#>hv0;rbej?R{u)23V5 zmg!UNmT8q2JT<&`YPu=i($vyCt04uI2us9A$8w`nVFZunBVL+SJC9VMe9N$x-g#!ZzSL^ zmTMi@sfMl#iIC1v#%NtggjOPK&FPkUT^j;*u|QA@Sy?)dNSsJwhEaVOZt;6o&=(6t ziDxKp^kRW1wSvIaEh`)9+S;HR%vF7le*gRSTxf)X=PEt5!$^t4S?GP^FqJ3$&N%D-cY` z=fxpMif;~X#4uuhRmIH;Wdi1b6Uu@@4`3N;q?8RUdfLO!H3*H;RV6CWfI93e?XI4@ zH@eGBHg&HE%5`mkp=FF=EQlZLixk;`7 zCyQ?JDU+7lM)&fe!=co8cOSakHd;Vs?1q;APs?qi6%)a0so$9>x-4CrUgjKR9RE_&0%3xp14KJnnFz#JvQjF zrjS&IQbo&}LfVKh)zMFb%8U@C_x%8WF#IWxA4o?tBf@PJae~f_2#aMXPc$3XG}{8AjT8X_-vXlY7;22z77*0~2pX^rY1cv* z++;j|-)E_l5sN2yh=E1rt6KXQp~ymwz$tiCzDh7@TSuIoTrkS(+htLpA1BSX$bhVZ5 zu~+q!^1@|T+yc-WMR_YWSn)ZU&^L;5fed})>5ZaXksvrqnV2eB1W-;aPGVZ@d8Ko` zTt4UZ`@`U`x_PXSIf@E2%iX$hDnLTF6Md zy_*42a5$mD^;qo69pu6VxFN@ZhU>#^{Q;|;ZwMW(4~yjwoOv%g4cCX&5rL{>hG8Da zcfjJK0y^Q$eCA2a$^C*%6l2KjSJhb226(`JMU`BN#^57SdWnOmTHdT+6`;y&u>Nc3 zJDHw>UG%u^=4%OBW)o=W4<5E1@hb`FGMj)*h8hD}W)sjwgs6@?!9_qzu(r+ex@9Qw zeY>&3I}>WI2vn~IYY>vmP^{KRKWiYkp!Qk(7sH2(O)osjnC`Q%Mv5Ro@3Sy@48@1; zvoJk?pn(dK`EnQXaT6O2l7*To0t6i-3rS@tMl?tk(gp}7NJkfGKz&^?aez;5qN5vV ztO%42M>imsp-732cR(K?sGuURNOpe&{1rG4tkK9T`mqlV^0C<$ZtDLEY|lYQUa6q9 z10*!kiF$~GQO8x#`9KaV^DHKx^)y+y9$Imr6bGRL*fGTd)*gT*6`6q^{)P2^_$1JI zmQ@z^KzZn3gz$64Vd#Py20;{MyE(K@&sr|1@GbyRIWcQpy#XndGme|j30qW8{ptzw zejZMLsE9@u3JofU&qeKb^laMxT?~RszCzvK(xUB^J*$|tT%pYK3w!>XjKc`w=P4Wx z@+UMhXUS(Qu(wnYMfoiqTKP!J1r<0TQ*bGhNzT35oy*K(5$hRkjkLFG5geEE57*be?0p2 ziL-aVa`BFDxQ{F!am^89U*6gE#lQUT3%6akeeTLHec5hx<+D*tk zUFG4B{)*6kJaK46eFv~5sgd+Ew9IR95JLil>Lm%Hid+U}VW0V0k#PDUwTI%`ijaP6 z2tZ0SHILAZs)GT9r}|AQ)!y1*{m|faxMCjqJLhxjReK4ar>Z;>zqcZwgTFneDvg_AP|X4=6kgMJfk>^;ETf{~DE8Kl6C(R2Tvv z5;Yr3bT8?a%A%>wLG?1i>iwQ?{jjF74EI*VG9hCbme$Y(QnaxQD+q+>7Om@gt`xJC z15eH+Xr2`LqOxv^OkS3Zhsne=6h^!D002>Vg}y~kt5YaBMHFBvr?EpB95X*sxnO=4dt{ns9^y@cB?kSDU9Ai#})4#KfZStA@$^1kHqh(Naz)b z63TTn&)4i$8U`RDw33gneY|oIlRLk<8U6)_5Nk z3EV!|%$R>U93+gAnW3PL8c!YwJguu6bY}qBESK+`^YwPr>PG$vic}`Dx{*t4sD+GL z-N=;$LbhL{t;8GT;eh3{zxxrfmJ=i1S>8*L%uAE-Ai0!=(wMFJ_<;!Aq>(^ZxMU@L z!}El^<04dd%ZkMHCon@3Rm@wZ$a4`<8eY9Nqqs=XAMNsk=YDr0CghQ9MEL zrmm9+a;M3WlXX{Q@R|gyo=jXr&0j%%K!7m4Dj`hKX?ye0DG|Mu4Mnz$`Q16o2&?iG z+g5S#1D~M??DYu)8<5#hYTeq@2Gj&XS7XvJzHA6x&F)p!J`qX7|Mkfa5O(EB!!aMC z2yX&O!!be|O0PU=I7Uw(e6Q4Jc|*#{ZZi6RH(3PUwDj$c{0K#Kcj~vhks=!k?xw*F z3=p>Z0`8&lV?M&2J5MF3$^-7feu{i15O5F5X(*KPfO}9iAQCmkJ?H1FuA);$RYNGP zLs~KPlY`GBP|D+;QJ7YpE#o9OJMTN}Um%lLXnFGP5FOz+- zht6}98_3z0TuZQ%XR=pyRRl1BO!g{K4K;@HO!g}64ge~>QUsO%{e#nLP#Nkvl&K7f zdVo(%Jnl%yUUESV#j#6e5P)dZ7*EGEF0?It)v~bZc*5UKCM-t8)5UIz zBqkA07sWJ`!-#mgs2LE2`id`nBhO~$UH48W@X3oWaBD^8^cP<+Rm}4Y<;53RAt1o? znzPwx#uTnW&)sUR?Dep--MAA?vM3VggwbGHDIPQl2FtQxFJI zedi7YlwxE)w(VX*ru^Iiv6mu|37k71N@*yG@^c47#efLZH!Cl*Ju8dq7X1}G!#1dtW1>x0#4tK$gISf67H!8U;;ZLVMz@oQNAM* zRt|_reLEt)LZ#e-&80lLl+5LpusgGo93v^;5eaov80JmwDm>XZK)yG+JU0QXSjFM$bJRLQ)!bi{;AlsZs=6L#N@NVO5W7vTX`R98KL zDly0yKv5wbBJw>#xvA;XyX|Y_JG4Pq#qET+qG?@;8#x2X)P)p zV4fnMxc^NW+OO`b$mUi0v$(2dH5APaibDZJrryYo7#rnWeq!W<#ArrGcEp~FR3;SJ z5hXR0$>_+As2mWHy;^PMN^6TF#B}!l{YwPJ@saPP-$jwZZQ3`Tv0>y&nYX&J;-5PZ zbove?W8>3ib&1JdT`zo}kS9NoEZ9#GzXT2>3(9Gz0hAv|7F6wkAaRw|wb;&=@Ir>U zi7F)WenXo?F6RM9B3BKFM7^%z>YUwIo$0^f)_ZEYhGI`e8WVC2MM(|iG1@g0l>;KO zq}4TK2JxZ)c!)qaCdxTc&QO_C(E`90#7598)56X8f*nHl7_`|P~ zjXZg}f1sNpa0#UQ2gEeg%E_ym0-6C)=xQ@@6>XT28}q;y2z_IvFP`6Ak-9C~%^X+8 zJVUoW`gsFEcD26q7*^V4bd`14ZnZMq(MQrPyY*QDU$gvS#C(b(lQ-&*QP88n>UBLQbLN+U0Ui z+nUco0* z5C~C?_Ap#l%FlF&u!ofniX0|j4+Vt|7<*XJ2?#cQTg&7uZG0*~d%v)uH)HPrF@*B1 zY2UP8p@{p8f&SBK$vQb z;t;jYw#%Mt=R*~J2fgeNODT`yNIEMbnm`msBCMf+%A+_E{ebYiPT#IgNU2q>RYco3 zu9t3iG8=7A@GTpU*+f{K7B{z0(5pU45!{>f@7^dq1M`+Zo8Q$i0U@n@ZA3X_n>ZT^ z{VHoC(od1rwBdO_@{kbepva#7kdUa5dCbW}&h3&!5?l_2~ z*2>Sa6MHFAn7~a1aOdBNFL=7#NtX6k z#PZ__tYD<_8VYD}5JLilszx7?j2v60^j>owv6B%#Vx@y3iwXFMfDww?V|P>q75Tl- z@~uMNN0B}KTZLRiL&=kG6>_zJz|*%NL8_eVK=wxvn0P9wx6Jxb?En5G)({jG^=yiilpX zDI};Qv!S@UgB%(lbal@20d&vG6|>@{1h0SS>+$ovq_-ljNzC&SX$=K6VxE^M2!!aB zNm*CC_iPojOLIkG<8S==-yoc(72aUsBNSoXnY3*cL^hPzO@kX4AZ)Ks3fl$6-kj?c z3+P(P)s~yHIs)ES$@H<1{G08ql5#(Ev23aP%p+s+amqP`FHuNzpk$xZE>k)p) z;!xZu3&*es4O;`VRq#_3an^MVi=xzqf-D@vqNpek!rNejrIE@lDdzm4^DE0XfA^cj z7N4T7`nl7h}z-4FnLCz}GEAQW?ApgkvdOw-DP`kxO0IEyT)dsO1!{ zTZmN;h|DXJ@@CJr``c_6PubjVPP+6v#BNSjcwPA?C}MhD(&m#(Y$&XyK@AHKvWdkF z&Mn(+yPa_Z^=5y}|9B8_1K}eSp-nt)Ac$-zwJLD~K}#TPlk!XhH!dB2`y+$!Oi@=w zN)z`?sg?((XNt4~q7wL!=}4D`E%BfE`R53c!XeX9-4tomb;xv-n1U8pB(W1ItYD zJ-_}NAzL2aiuwdam=g$ZMM-QZzw+=_l#)QmYD_xB(N|P^+?zugTT$ki=fcX94x>Lq z5!(ck4x?o@6kK`IVYHe+=kKM!m8_}hnn&l%BpbE zLroPAG*sKV`^&kEmFK8VeE+~8P^JA%L6ypT0HaD(1VU9`t_wD*=v;ZvE-cDB9Ub^K zH@4RL@z-x7l*)5ms{1Munn12gwXBBHDbIDORu70wjWscgal8xhC=YS*K!CjVRn}V( z&je~>WYQW6sJtddrXUcaz$ZEh?h93lSkMl~U^h&$4u_fJ{Zf)|3qwnRs{?Nq*f1H0KImbkvToUM}h+G26B>^!FHE{Cel7MDF z6awGOE7%66O}mGzh;S*v@ZEP14uzX}qdO{+sOx6lXh98SQMj2mS~nmXHD)g_bx`0Y zx21C9kbEW5H{%0jDwbD0M}CANq6uU#M~ZAHtn%#TNG*Y|1wIB(cnt%ZvDL}w&#ot+ z3di6@^ipI~*D-hzQW}b*a136AVn76HjNUADy1JsHH#{#?9=(zERzxy^=#5NTL(!B+ zZ)6GrAqsu1VBm$TAR4TIXXbLkjqfGqQn*&IR(C~6bzLi1OI$-K6|NPmr6CZe`W8g= z-9wa?9kJ5$j9-ll@&!>yHy|4I1&OFW^X-lvVXKg{C=U_^ zx+#*FK#(XPrlB0lgG2$%fGFImEf!9-S?M)726q&D1Bk;NUpcn+KJ(Lu5+db^6r_V9h58dI zM1{=T?u5&U zq-p_ycdfpHn-wK|R6-dgSO^Ez3%WA%mw)Uqg5y-F`&{FL6dC=5{zXj)cJvFP9{2Ql6ZF zC?cod$K)a!YVzbhCRYmxJm7nO1s4+CrMRG&as0ywn58>EdnuWRg?oSHofOg2b?>iS zNJHTh?){bP1w`LQ*nOD~w=XGrw(AJh2_yF$M}Ql}U1Qcxk+HI#x&$Vcp;WDwJYgV^ zTv(7$QAygbazQ6t1r6AoLne^i@3CClaC1ehw&=ksES;frt&e`*K#-MSAzPCxwoE}a zOI^9LC*D`tx3DjhdluiNI)j5k% zn=)R9J@NLMy?gfm&zN^`=yZ)-b9o(D0eCelljsM!5-`=|anO{Ei z{H;$;A2soy8%DTi{PmKXw(tE--;vk<=&-&Or@lJnh+V%wy|b zyZfPgKlP#Y9h2^{CyjgiXLG&v&g55K-*&;5NAA6O?kOi+b9AdcW=@7y=%t|R_&*Z1x`DZk;m8N-)8 zb?{rG7jGPPOJ#}#iB*IZX?6Qc9xgR6b)$M?@z9y3fBb-$u?e+0`PgA! zEZ!f3jaAeV(opm=we%9{YpI`urk_4~>xFN-)}FCdi9UR*2VM=}uOUkx)ocJu`N7Hx z!ZfMa)mj^PHL^Fq0k@W%%q!c)8veC9iF_SByg9lNpAnWuA ziv^C`#G1W)=&*H+_wHazfon>s%-+y)@v=1)6A7%Nn+I}m5mAQXChT&sy(muJagnW= gnTsb5^x&dM;gmreT1IYNq(UNKO={~1g}L + + + False + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\Yao Chang Yu\.nuget\packages\ + PackageReference + 5.11.0 + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.g.targets b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.g.targets new file mode 100644 index 00000000..d212750c --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/WebApiNetCore31.csproj.nuget.g.targets @@ -0,0 +1,6 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.assets.json b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.assets.json new file mode 100644 index 00000000..91d6469b --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.assets.json @@ -0,0 +1,88 @@ +{ + "version": 3, + "targets": { + ".NETCoreApp,Version=v3.1": {} + }, + "libraries": {}, + "projectFileDependencyGroups": { + ".NETCoreApp,Version=v3.1": [ + "Autofac.Extensions.DependencyInjection >= 7.1.0" + ] + }, + "packageFolders": { + "C:\\Users\\Yao Chang Yu\\.nuget\\packages\\": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "D:\\src\\sample.dotblog\\DI\\Lab.MsDIForAutofac\\WebApiNetCore31\\WebApiNetCore31.csproj", + "projectName": "WebApiNetCore31", + "projectPath": "D:\\src\\sample.dotblog\\DI\\Lab.MsDIForAutofac\\WebApiNetCore31\\WebApiNetCore31.csproj", + "packagesPath": "C:\\Users\\Yao Chang Yu\\.nuget\\packages\\", + "outputPath": "D:\\src\\sample.dotblog\\DI\\Lab.MsDIForAutofac\\WebApiNetCore31\\obj\\", + "projectStyle": "PackageReference", + "configFilePaths": [ + "C:\\Users\\Yao Chang Yu\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "netcoreapp3.1" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {} + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "dependencies": { + "Autofac.Extensions.DependencyInjection": { + "target": "Package", + "version": "[7.1.0, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.AspNetCore.App": { + "privateAssets": "none" + }, + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\5.0.400\\RuntimeIdentifierGraph.json" + } + } + }, + "logs": [ + { + "code": "NU1101", + "level": "Error", + "message": "Unable to find package Autofac.Extensions.DependencyInjection. No packages exist with this id in source(s): Microsoft Visual Studio Offline Packages", + "libraryId": "Autofac.Extensions.DependencyInjection", + "targetGraphs": [ + ".NETCoreApp,Version=v3.1" + ] + } + ] +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.nuget.cache b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.nuget.cache new file mode 100644 index 00000000..f0606a64 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WebApiNetCore31/obj/project.nuget.cache @@ -0,0 +1,18 @@ +{ + "version": 2, + "dgSpecHash": "+oR7kmoX58hU75JnQMAzXJl+PfV8Klr4xZc4raw2Ndh3DuN5Ev+x0AdClXbVyIWS+ICIvv8tTLq6Y376zhkbMQ==", + "success": false, + "projectFilePath": "D:\\src\\sample.dotblog\\DI\\Lab.MsDIForAutofac\\WebApiNetCore31\\WebApiNetCore31.csproj", + "expectedPackageFiles": [], + "logs": [ + { + "code": "NU1101", + "level": "Error", + "message": "Unable to find package Autofac.Extensions.DependencyInjection. No packages exist with this id in source(s): Microsoft Visual Studio Offline Packages", + "libraryId": "Autofac.Extensions.DependencyInjection", + "targetGraphs": [ + ".NETCoreApp,Version=v3.1" + ] + } + ] +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/App.config b/DI/Lab.MsDIForAutofac/WinFormNet48/App.config new file mode 100644 index 00000000..3916e0e4 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.Designer.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.Designer.cs new file mode 100644 index 00000000..18e39a8b --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.Designer.cs @@ -0,0 +1,59 @@ +namespace WinFormNet48 +{ + partial class Form2 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(693, 394); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // Form2 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.button1); + this.Name = "Form2"; + this.Text = "Form2"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.cs new file mode 100644 index 00000000..6222446f --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WinFormNet48 +{ + public partial class Form2 : Form + { + public Form2() + { + InitializeComponent(); + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.resx b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Form2.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.Designer.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.Designer.cs new file mode 100644 index 00000000..e4bf0689 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.Designer.cs @@ -0,0 +1,39 @@ +namespace WinFormNet48 +{ + partial class Form3 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Text = "Form3"; + } + + #endregion + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.cs new file mode 100644 index 00000000..f170710b --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Form3.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WinFormNet48 +{ + public partial class Form3 : Form + { + public Form3() + { + InitializeComponent(); + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.Designer.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.Designer.cs new file mode 100644 index 00000000..fb1bb09c --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.Designer.cs @@ -0,0 +1,95 @@ +namespace WinFormNet48 +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.button2 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.button2); + this.splitContainer1.Panel1.Controls.Add(this.button1); + this.splitContainer1.Size = new System.Drawing.Size(800, 450); + this.splitContainer1.SplitterDistance = 266; + this.splitContainer1.TabIndex = 0; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(71, 78); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 1; + this.button2.Text = "button2"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(71, 37); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.splitContainer1); + this.Name = "MainForm"; + this.Text = "Form1"; + this.splitContainer1.Panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button1; + } +} + diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.cs new file mode 100644 index 00000000..c9ea9f2d --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace WinFormNet48 +{ + public partial class MainForm : Form + { + private readonly Dictionary _subFormLook = new Dictionary(); + private Form _previousForm; + + public MainForm() + { + this.InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + this.Show("Form2"); + } + + private void button2_Click(object sender, EventArgs e) + { + this.Show("Form3"); + } + + private void Show(string name) + { + Form subForm; + if (this._subFormLook.ContainsKey(name) == false) + { + subForm = new Form2(); + subForm.TopLevel = true; + subForm.Visible = true; + subForm.WindowState = FormWindowState.Maximized; + subForm.Dock = DockStyle.Fill; + //subForm.ControlBox = false; + this._subFormLook.Add(name, subForm); + //this.splitContainer1.Panel2.Controls.Add(subForm); + } + + subForm = this._subFormLook[name]; + + subForm.Show(); + + //if (this._previousForm != null) + //{ + // if (this._previousForm.Name != subForm.Name) + // { + // this._previousForm.Hide(); + // } + //} + + //this._previousForm = subForm; + } + } +} \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.resx b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Program.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Program.cs new file mode 100644 index 00000000..149cd059 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WinFormNet48 +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/AssemblyInfo.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5cabfbce --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WinFormNet48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WinFormNet48")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0fb52124-ec0f-4ba8-975e-8b3656dc0def")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.Designer.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.Designer.cs new file mode 100644 index 00000000..bf954d2b --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinFormNet48.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinFormNet48.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.resx b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.resx new file mode 100644 index 00000000..ffecec85 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.Designer.cs b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.Designer.cs new file mode 100644 index 00000000..f43f01d5 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WinFormNet48.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.settings b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.settings new file mode 100644 index 00000000..abf36c5d --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/WinFormNet48.csproj b/DI/Lab.MsDIForAutofac/WinFormNet48/WinFormNet48.csproj new file mode 100644 index 00000000..21afa8cf --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/WinFormNet48.csproj @@ -0,0 +1,106 @@ + + + + + Debug + AnyCPU + {0FB52124-EC0F-4BA8-975E-8B3656DC0DEF} + WinExe + WinFormNet48 + WinFormNet48 + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\linq2db.3.1.5\lib\net46\linq2db.dll + + + ..\packages\linq2db4iSeries.3.1.5\lib\net45\LinqToDB.DataProvider.DB2iSeries.dll + + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + Form + + + Form2.cs + + + Form + + + Form3.cs + + + + + Form2.cs + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/DI/Lab.MsDIForAutofac/WinFormNet48/packages.config b/DI/Lab.MsDIForAutofac/WinFormNet48/packages.config new file mode 100644 index 00000000..3636f3c7 --- /dev/null +++ b/DI/Lab.MsDIForAutofac/WinFormNet48/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From 0c60abb38afdb7a54db4db73eced495ad049149c Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 23 Oct 2021 15:01:01 +0800 Subject: [PATCH 119/267] add project --- .../CustomTestServer.cs | 25 +++++++++ .../Lab.Test.WebApi.Net5.TestProject.csproj | 22 ++++++++ .../SurveyWebApplicationFactory.cs | 32 ++++++++++++ .../Controllers/DemoController.cs | 47 +++++++++++++++++ .../Lab.Test.WebApi.Net5/FileProvider.cs | 12 +++++ .../Lab.Test.WebApi.Net5/IFileProvider.cs | 7 +++ .../Lab.Test.WebApi.Net5.csproj | 11 ++++ .../Lab.Test.WebApi.Net5/Program.cs | 19 +++++++ .../Properties/launchSettings.json | 31 +++++++++++ .../Lab.Test.WebApi.Net5/Startup.cs | 51 +++++++++++++++++++ .../appsettings.Development.json | 9 ++++ .../Lab.Test.WebApi.Net5/appsettings.json | 10 ++++ WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.sln | 22 ++++++++ 13 files changed, 298 insertions(+) create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/CustomTestServer.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/Lab.Test.WebApi.Net5.TestProject.csproj create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/FileProvider.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/IFileProvider.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Program.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Properties/launchSettings.json create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Startup.cs create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.Development.json create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.json create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.sln diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/CustomTestServer.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/CustomTestServer.cs new file mode 100644 index 00000000..a420a623 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/CustomTestServer.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; + +namespace Lab.Test.WebApi.Net5.TestProject +{ + public class CustomTestServer : WebApplicationFactory + { + private void ConfigureServices(IServiceCollection services) + { + services.AddScoped(p => + { + var fileProvider = Substitute.For(); + fileProvider.Name().Returns("Fake FileProfile"); + return fileProvider; + }); + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(this.ConfigureServices); + } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/Lab.Test.WebApi.Net5.TestProject.csproj b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/Lab.Test.WebApi.Net5.TestProject.csproj new file mode 100644 index 00000000..53390344 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/Lab.Test.WebApi.Net5.TestProject.csproj @@ -0,0 +1,22 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + + diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs new file mode 100644 index 00000000..bc1a2542 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.Test.WebApi.Net5.TestProject +{ + [TestClass] + public class SurveyWebApplicationFactory + { + [TestMethod] + public void CustomTestServer() + { + var server = new CustomTestServer(); + var httpClient = server.CreateClient(); + var url = "demo/file"; + var response = httpClient.GetAsync(url).Result; + var result = response.Content.ReadAsStringAsync().Result; + Console.WriteLine(result); + } + + [TestMethod] + public void WebApplicationFactory基本用法() + { + var server = new WebApplicationFactory(); + var httpClient = server.CreateClient(); + var url = "demo/file"; + var response = httpClient.GetAsync(url).Result; + var result = response.Content.ReadAsStringAsync().Result; + Console.WriteLine(result); + } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs new file mode 100644 index 00000000..afa35b7b --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs @@ -0,0 +1,47 @@ +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Lab.Test.WebApi.Net5.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DemoController : ControllerBase + { + private readonly IFileProvider _fileProvider; + private readonly ILogger _logger; + + public DemoController(ILogger logger, + IFileProvider fileProvider) + { + this._logger = logger; + this._fileProvider = fileProvider; + } + + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(QueryResponse))] + public IActionResult Get(CancellationToken cancel) + { + return this.Ok(new QueryResponse + { + Message = "Hello" + }); + } + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(QueryResponse))] + [Route("file")] + public IActionResult GetFile(CancellationToken cancel) + { + return this.Ok(new QueryResponse + { + Message = this._fileProvider.Name() + }); + } + } + + public class QueryResponse + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/FileProvider.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/FileProvider.cs new file mode 100644 index 00000000..24f0e1f8 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/FileProvider.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace Lab.Test.WebApi.Net5 +{ + public class FileProvider:IFileProvider + { + public string Name() + { + return nameof(FileProvider); + } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/IFileProvider.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/IFileProvider.cs new file mode 100644 index 00000000..929301aa --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/IFileProvider.cs @@ -0,0 +1,7 @@ +namespace Lab.Test.WebApi.Net5 +{ + public interface IFileProvider + { + string Name(); + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj new file mode 100644 index 00000000..39eb5ab3 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Program.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Program.cs new file mode 100644 index 00000000..3df6d830 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Program.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Lab.Test.WebApi.Net5 +{ + public class Program + { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } + + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Properties/launchSettings.json b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Properties/launchSettings.json new file mode 100644 index 00000000..acf4a732 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20722", + "sslPort": 44330 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Lab.Test.WebApi.Net5": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Startup.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Startup.cs new file mode 100644 index 00000000..f39a8829 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Startup.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +namespace Lab.Test.WebApi.Net5 +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Lab.Test.WebApi.Net5 v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Lab.Test.WebApi.Net5", Version = "v1" }); + }); + + services.AddScoped(p => new FileProvider()); + services.AddScoped(p => (IFileProvider)p.GetService()); + } + } +} \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.Development.json b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.json b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.sln b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.sln new file mode 100644 index 00000000..09cca1af --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Test.WebApi.Net5", "Lab.Test.WebApi.Net5\Lab.Test.WebApi.Net5.csproj", "{B97FA43E-B196-4800-9AF6-6F5F7B412D1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Test.WebApi.Net5.TestProject", "Lab.Test.WebApi.Net5.TestProject\Lab.Test.WebApi.Net5.TestProject.csproj", "{B0E91B25-C99E-44D7-9C70-27DF6BF0DBEC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B97FA43E-B196-4800-9AF6-6F5F7B412D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B97FA43E-B196-4800-9AF6-6F5F7B412D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B97FA43E-B196-4800-9AF6-6F5F7B412D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B97FA43E-B196-4800-9AF6-6F5F7B412D1D}.Release|Any CPU.Build.0 = Release|Any CPU + {B0E91B25-C99E-44D7-9C70-27DF6BF0DBEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0E91B25-C99E-44D7-9C70-27DF6BF0DBEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0E91B25-C99E-44D7-9C70-27DF6BF0DBEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0E91B25-C99E-44D7-9C70-27DF6BF0DBEC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From ab9dffc0dc6761c7c59e59b8014aa9302da93783 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 23 Oct 2021 15:47:58 +0800 Subject: [PATCH 120/267] refactor --- .../SurveyWebApplicationFactory.cs | 8 ++++---- .../Controllers/DemoController.cs | 18 ++---------------- .../Lab.Test.WebApi.Net5.csproj | 1 + .../ServiceModels/QueryResponse.cs | 7 +++++++ 4 files changed, 14 insertions(+), 20 deletions(-) create mode 100644 WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/ServiceModels/QueryResponse.cs diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs index bc1a2542..05c45143 100644 --- a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5.TestProject/SurveyWebApplicationFactory.cs @@ -12,7 +12,7 @@ public void CustomTestServer() { var server = new CustomTestServer(); var httpClient = server.CreateClient(); - var url = "demo/file"; + var url = "demo"; var response = httpClient.GetAsync(url).Result; var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); @@ -22,9 +22,9 @@ public void CustomTestServer() public void WebApplicationFactory基本用法() { var server = new WebApplicationFactory(); - var httpClient = server.CreateClient(); - var url = "demo/file"; - var response = httpClient.GetAsync(url).Result; + var client = server.CreateClient(); + var url = "demo"; + var response = client.GetAsync(url).Result; var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); } diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs index afa35b7b..f28586a7 100644 --- a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Controllers/DemoController.cs @@ -1,4 +1,5 @@ using System.Threading; +using Lab.Test.WebApi.Net5.ServiceModels; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -21,17 +22,7 @@ public DemoController(ILogger logger, [HttpGet] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(QueryResponse))] - public IActionResult Get(CancellationToken cancel) - { - return this.Ok(new QueryResponse - { - Message = "Hello" - }); - } - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(QueryResponse))] - [Route("file")] - public IActionResult GetFile(CancellationToken cancel) + public IActionResult Get(CancellationToken cancel = default) { return this.Ok(new QueryResponse { @@ -39,9 +30,4 @@ public IActionResult GetFile(CancellationToken cancel) }); } } - - public class QueryResponse - { - public string Message { get; set; } - } } \ No newline at end of file diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj index 39eb5ab3..a3a18f0e 100644 --- a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/Lab.Test.WebApi.Net5.csproj @@ -2,6 +2,7 @@ net5.0 + enable diff --git a/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/ServiceModels/QueryResponse.cs b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/ServiceModels/QueryResponse.cs new file mode 100644 index 00000000..9d6a0de6 --- /dev/null +++ b/WebAPI/Lab.Test.WebApi/Lab.Test.WebApi.Net5/ServiceModels/QueryResponse.cs @@ -0,0 +1,7 @@ +namespace Lab.Test.WebApi.Net5.ServiceModels +{ + public class QueryResponse + { + public string Message { get; set; } + } +} \ No newline at end of file From f331ac519493f3b1461d44e7723ee9eba3b40020 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 29 Nov 2021 15:26:52 +0800 Subject: [PATCH 121/267] first add --- .../GracefulShutdownService.cs | 47 ++++++++++++++++ .../GracefulShutdownService_Fail.cs | 40 ++++++++++++++ .../Lab.GracefulShutdown.Net6.csproj | 13 +++++ .../Lab.GracefulShutdown.Net6/Program.cs | 53 +++++++++++++++++++ .../Properties/launchSettings.json | 10 ++++ .../Lab.GracefulShutdown.sln | 16 ++++++ 6 files changed, 179 insertions(+) create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService.cs create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService_Fail.cs create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Lab.GracefulShutdown.Net6.csproj create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Program.cs create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Properties/launchSettings.json create mode 100644 Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.sln diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService.cs b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService.cs new file mode 100644 index 00000000..0b5a46eb --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.Hosting; + +namespace Lab.GracefulShutdown.Net6; + +internal class GracefulShutdownService : IHostedService +{ + private readonly IHostApplicationLifetime _appLifetime; + private Task _backgroundTask; + private bool _stop; + + public GracefulShutdownService(IHostApplicationLifetime appLifetime) + { + this._appLifetime = appLifetime; + } + + public Task StartAsync(CancellationToken cancel) + { + Console.WriteLine($"{DateTime.Now} 服務啟動中..."); + + this._backgroundTask = Task.Run(async () => { await this.ExecuteAsync(cancel); }, cancel); + + return Task.CompletedTask; + } + + public async Task StopAsync(CancellationToken cancel) + { + Console.WriteLine($"{DateTime.Now} 服務停止中..."); + + this._stop = true; + await this._backgroundTask; + + Console.WriteLine($"{DateTime.Now} 服務已停止"); + } + + private async Task ExecuteAsync(CancellationToken cancel) + { + Console.WriteLine($"{DateTime.Now} 服務已啟動!"); + + while (!this._stop) + { + Console.WriteLine($"{DateTime.Now} 服務運行中..."); + await Task.Delay(TimeSpan.FromSeconds(1), cancel); + } + + Console.WriteLine($"{DateTime.Now} 服務已完美的停止(Graceful Shutdown)"); + } +} \ No newline at end of file diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService_Fail.cs b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService_Fail.cs new file mode 100644 index 00000000..38004cce --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/GracefulShutdownService_Fail.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Hosting; + +namespace Lab.GracefulShutdown.Net6; + +internal class GracefulShutdownService_Fail : IHostedService +{ + private readonly IHostApplicationLifetime _appLifetime; + private bool _stop; + + public GracefulShutdownService_Fail(IHostApplicationLifetime appLifetime) + { + this._appLifetime = appLifetime; + } + + public async Task StartAsync(CancellationToken cancel) + { + Console.WriteLine($"{DateTime.Now} 服務啟動中..."); + await this.ExecuteAsync(cancel); + } + + public Task StopAsync(CancellationToken cancel) + { + this._stop = true; + Console.WriteLine("服務關閉"); + return Task.CompletedTask; + } + + private async Task ExecuteAsync(CancellationToken cancel) + { + Console.WriteLine($"{DateTime.Now} 服務已啟動!"); + + while (!this._stop) + { + Console.WriteLine($"{DateTime.Now} 服務運行中..."); + await Task.Delay(TimeSpan.FromSeconds(1), cancel); + } + + Console.WriteLine($"{DateTime.Now} 服務已完美的停止(Graceful Shutdown)"); + } +} \ No newline at end of file diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Lab.GracefulShutdown.Net6.csproj b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Lab.GracefulShutdown.Net6.csproj new file mode 100644 index 00000000..1a94648c --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Lab.GracefulShutdown.Net6.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0 + enable + enable + + + + + + diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Program.cs b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Program.cs new file mode 100644 index 00000000..bec06acf --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Program.cs @@ -0,0 +1,53 @@ +using Lab.GracefulShutdown.Net6; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Runtime.Loader; + +var tcs = new TaskCompletionSource(); +var sigintReceived = false; + +Console.WriteLine("等待以下訊號 SIGINT/SIGTERM"); + +Console.CancelKeyPress += (sender, e) => +{ + e.Cancel = true; + Console.WriteLine("已接收 SIGINT (Ctrl+C)"); + tcs.SetResult(); + sigintReceived = true; +}; + +AssemblyLoadContext.Default.Unloading += ctx => +{ + if (!sigintReceived) + { + Console.WriteLine("已接收 SIGTERM"); + tcs.SetResult(); + } + else + { + Console.WriteLine("@AssemblyLoadContext.Default.Unloading,已處理 SIGINT,忽略 SIGTERM"); + } +}; + +AppDomain.CurrentDomain.ProcessExit += (sender, e) => +{ + if (!sigintReceived) + { + Console.WriteLine("已接收 SIGTERM"); + tcs.SetResult(); + } + else + { + Console.WriteLine("@AppDomain.CurrentDomain.ProcessExit,已處理 SIGINT,忽略 SIGTERM"); + } +}; + +await Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + // services.AddHostedService(); + services.AddHostedService(); + }) + .RunConsoleAsync(); +Console.WriteLine("下次再來唷~"); + diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Properties/launchSettings.json b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Properties/launchSettings.json new file mode 100644 index 00000000..b665f30e --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.Net6/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Lab.GracefulShutdown.Net6": { + "commandName": "Project", + "environmentVariables": { + } + } + } +} diff --git a/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.sln b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.sln new file mode 100644 index 00000000..d979b64b --- /dev/null +++ b/Graceful Shutdown/Lab.GracefulShutdown/Lab.GracefulShutdown.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.GracefulShutdown.Net6", "Lab.GracefulShutdown.Net6\Lab.GracefulShutdown.Net6.csproj", "{D21B2207-2D80-49B2-94A1-24234DBD9B8D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D21B2207-2D80-49B2-94A1-24234DBD9B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D21B2207-2D80-49B2-94A1-24234DBD9B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D21B2207-2D80-49B2-94A1-24234DBD9B8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D21B2207-2D80-49B2-94A1-24234DBD9B8D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 637a997a3a24114adc451ac51f4c1872a5684ebe Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 8 Dec 2021 19:28:19 +0800 Subject: [PATCH 122/267] feat: add project --- Configuration/Lab.Environment/.gitignore | 1 + .../Lab.Environment.ConsoleApp.NET48.csproj | 55 +++++++++++++++++++ .../Program.cs | 19 +++++++ .../Properties/AssemblyInfo.cs | 35 ++++++++++++ .../Lab.Environment.ConsoleApp.NET6.csproj | 10 ++++ .../Program.cs | 5 ++ .../Properties/launchSettings.json | 10 ++++ .../Lab.Environment/Lab.Environment.ps1 | 2 + .../Lab.Environment/Lab.Environment.sln | 22 ++++++++ Configuration/Lab.Environment/Taskfile.yml | 26 +++++++++ Configuration/Lab.Environment/global.json | 7 +++ 11 files changed, 192 insertions(+) create mode 100644 Configuration/Lab.Environment/.gitignore create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Lab.Environment.ConsoleApp.NET48.csproj create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Program.cs create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Properties/AssemblyInfo.cs create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Lab.Environment.ConsoleApp.NET6.csproj create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Program.cs create mode 100644 Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Properties/launchSettings.json create mode 100644 Configuration/Lab.Environment/Lab.Environment.ps1 create mode 100644 Configuration/Lab.Environment/Lab.Environment.sln create mode 100644 Configuration/Lab.Environment/Taskfile.yml create mode 100644 Configuration/Lab.Environment/global.json diff --git a/Configuration/Lab.Environment/.gitignore b/Configuration/Lab.Environment/.gitignore new file mode 100644 index 00000000..c027a961 --- /dev/null +++ b/Configuration/Lab.Environment/.gitignore @@ -0,0 +1 @@ +secrets.env \ No newline at end of file diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Lab.Environment.ConsoleApp.NET48.csproj b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Lab.Environment.ConsoleApp.NET48.csproj new file mode 100644 index 00000000..099fd9b8 --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Lab.Environment.ConsoleApp.NET48.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {BB8D3ED0-BF9F-4910-9231-717DD0577FB5} + Exe + Properties + Lab.Environment.ConsoleApp.NET48 + Lab.Environment.ConsoleApp.NET48 + v4.8 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Program.cs b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Program.cs new file mode 100644 index 00000000..e6a16b3a --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Program.cs @@ -0,0 +1,19 @@ + +using System; + +namespace Lab.Environment.ConsoleApp.NET48 +{ + internal class Program + { + public static void Main(string[] args) + { + var appEnv = System.Environment.GetEnvironmentVariable("APP_ENV"); + var scoopPath = System.Environment.GetEnvironmentVariable("scoop"); + + if (string.IsNullOrWhiteSpace(appEnv) == false) + { + Console.WriteLine(appEnv); + } + } + } +} \ No newline at end of file diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Properties/AssemblyInfo.cs b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..814f8719 --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET48/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Lab.Environment.ConsoleApp.NET48")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Lab.Environment.ConsoleApp.NET48")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("BB8D3ED0-BF9F-4910-9231-717DD0577FB5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Lab.Environment.ConsoleApp.NET6.csproj b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Lab.Environment.ConsoleApp.NET6.csproj new file mode 100644 index 00000000..b9de0634 --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Lab.Environment.ConsoleApp.NET6.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Program.cs b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Program.cs new file mode 100644 index 00000000..f6ff2edf --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Program.cs @@ -0,0 +1,5 @@ +using System; + +var appEnv = Environment.GetEnvironmentVariable("APP_ENV"); +var scoopPath = Environment.GetEnvironmentVariable("scoop"); +Console.ReadKey(); \ No newline at end of file diff --git a/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Properties/launchSettings.json b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Properties/launchSettings.json new file mode 100644 index 00000000..e1fbded0 --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ConsoleApp.NET6/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Lab.Environment.ConsoleApp.NET6": { + "commandName": "Project", + "environmentVariables": { + } + } + } +} diff --git a/Configuration/Lab.Environment/Lab.Environment.ps1 b/Configuration/Lab.Environment/Lab.Environment.ps1 new file mode 100644 index 00000000..ca7050f6 --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.ps1 @@ -0,0 +1,2 @@ +$Env:APP:ENV = "QA" +./Lab.Environment.sln \ No newline at end of file diff --git a/Configuration/Lab.Environment/Lab.Environment.sln b/Configuration/Lab.Environment/Lab.Environment.sln new file mode 100644 index 00000000..9623d29e --- /dev/null +++ b/Configuration/Lab.Environment/Lab.Environment.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Environment.ConsoleApp.NET48", "Lab.Environment.ConsoleApp.NET48\Lab.Environment.ConsoleApp.NET48.csproj", "{BB8D3ED0-BF9F-4910-9231-717DD0577FB5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Environment.ConsoleApp.NET6", "Lab.Environment.ConsoleApp.NET6\Lab.Environment.ConsoleApp.NET6.csproj", "{5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BB8D3ED0-BF9F-4910-9231-717DD0577FB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB8D3ED0-BF9F-4910-9231-717DD0577FB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB8D3ED0-BF9F-4910-9231-717DD0577FB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB8D3ED0-BF9F-4910-9231-717DD0577FB5}.Release|Any CPU.Build.0 = Release|Any CPU + {5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Configuration/Lab.Environment/Taskfile.yml b/Configuration/Lab.Environment/Taskfile.yml new file mode 100644 index 00000000..f4feaebd --- /dev/null +++ b/Configuration/Lab.Environment/Taskfile.yml @@ -0,0 +1,26 @@ +version: "3" +env: + GREETING: Hey, there! +dotenv: ["secrets.env"] +vars: + PATH: "/mnt/c/Users/Yao Chang Yu/scoop/apps/Rider-EAP/current/IDE/bin/" + #PATH: "C:\Users\Yao Chang Yu\scoop\apps\Rider-EAP\2021.3-EAP9-213.5744.160\IDE\bin\" +tasks: + print-os: + cmds: + - echo '{{OS}} {{ARCH}}' + - echo '{{if eq OS "windows"}}windows-command{{else}}unix-command{{end}}' + # This will be path/to/file on Unix but path\to\file on Windows + - echo '{{fromSlash "path/to/file"}}' + - echo '{{fromSlash "/mnt/c/Users/Yao Chang Yu/scoop/apps/Rider-EAP/current/IDE/bin/"}}' + greet: + desc: greet + cmds: + - echo $GREETING + rider: + desc: Rider + dir: "/mnt/c/Users/Yao Chang Yu/scoop/apps/Rider-EAP/current/IDE/bin/" + cmds: + - rider64.exe + env: + Url: http://localhost:9527 \ No newline at end of file diff --git a/Configuration/Lab.Environment/global.json b/Configuration/Lab.Environment/global.json new file mode 100644 index 00000000..f443bd42 --- /dev/null +++ b/Configuration/Lab.Environment/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file From 03084c5c99345ee57b079c678bfd813303d0e9c2 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 8 Dec 2021 20:01:16 +0800 Subject: [PATCH 123/267] refactor --- Configuration/Lab.Environment/Lab.Environment.ps1 | 2 +- Configuration/Lab.Environment/Taskfile.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Configuration/Lab.Environment/Lab.Environment.ps1 b/Configuration/Lab.Environment/Lab.Environment.ps1 index ca7050f6..1fd4ffcf 100644 --- a/Configuration/Lab.Environment/Lab.Environment.ps1 +++ b/Configuration/Lab.Environment/Lab.Environment.ps1 @@ -1,2 +1,2 @@ -$Env:APP:ENV = "QA" +$Env:APP_ENV = "QA" ./Lab.Environment.sln \ No newline at end of file diff --git a/Configuration/Lab.Environment/Taskfile.yml b/Configuration/Lab.Environment/Taskfile.yml index f4feaebd..b97d93b8 100644 --- a/Configuration/Lab.Environment/Taskfile.yml +++ b/Configuration/Lab.Environment/Taskfile.yml @@ -20,7 +20,7 @@ tasks: rider: desc: Rider dir: "/mnt/c/Users/Yao Chang Yu/scoop/apps/Rider-EAP/current/IDE/bin/" - cmds: + cmds: - rider64.exe env: Url: http://localhost:9527 \ No newline at end of file From ee8f6fa5107c004627f154c89003029c424fde55 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 3 Jan 2022 22:46:02 +0800 Subject: [PATCH 124/267] feat: add specflow project --- Test/Lab.AllureReport/.gitignore | 365 ++++++++++++++++++ Test/Lab.AllureReport/Lab.AllureReport.sln | 16 + .../Lab.AllureReport4Specflow/Calculation.cs | 9 + .../Lab.AllureReport4Specflow.csproj | 19 + .../Lab.AllureReport4Specflow/specflow.json | 10 + ...0\250\210\347\256\227\346\251\237.feature" | 9 + ...50\250\210\347\256\227\346\251\237Step.cs" | 37 ++ 7 files changed, 465 insertions(+) create mode 100644 Test/Lab.AllureReport/.gitignore create mode 100644 Test/Lab.AllureReport/Lab.AllureReport.sln create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/Calculation.cs create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/specflow.json create mode 100644 "Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" create mode 100644 "Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" diff --git a/Test/Lab.AllureReport/.gitignore b/Test/Lab.AllureReport/.gitignore new file mode 100644 index 00000000..a33719ce --- /dev/null +++ b/Test/Lab.AllureReport/.gitignore @@ -0,0 +1,365 @@ +### VisualStudio template +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +*.feature.cs diff --git a/Test/Lab.AllureReport/Lab.AllureReport.sln b/Test/Lab.AllureReport/Lab.AllureReport.sln new file mode 100644 index 00000000..625a34bd --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AllureReport4Specflow", "Lab.AllureReport4Specflow\Lab.AllureReport4Specflow.csproj", "{24AC2522-D7B4-4854-BE1E-18389125605F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {24AC2522-D7B4-4854-BE1E-18389125605F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24AC2522-D7B4-4854-BE1E-18389125605F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24AC2522-D7B4-4854-BE1E-18389125605F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24AC2522-D7B4-4854-BE1E-18389125605F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Calculation.cs b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Calculation.cs new file mode 100644 index 00000000..4880a559 --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Calculation.cs @@ -0,0 +1,9 @@ +namespace Lab.AllureReport4Specflow; + +public class Calculation +{ + public double Add(double firstNumber, double secondNumber) + { + return firstNumber + secondNumber; + } +} \ No newline at end of file diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj new file mode 100644 index 00000000..1c6c2cf4 --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/specflow.json b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/specflow.json new file mode 100644 index 00000000..06565c4c --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/specflow.json @@ -0,0 +1,10 @@ +{ + "language": { + "feature": "en-US" + }, + "stepAssemblies": [ + { + "assembly": "Allure.SpecFlowPlugin" + } + ] +} \ No newline at end of file diff --git "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" new file mode 100644 index 00000000..9c952f95 --- /dev/null +++ "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" @@ -0,0 +1,9 @@ +Feature: 計算機 + Simple calculator for adding two numbers + +@mytag +Scenario: Add two numbers + Given the first number is 50 + And the second number is 70 + When the two numbers are added + Then the result should be 120 \ No newline at end of file diff --git "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" new file mode 100644 index 00000000..e1ff71df --- /dev/null +++ "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TechTalk.SpecFlow; + +namespace Lab.AllureReport4Specflow; + +[Binding] +public class 計算機Step : Steps +{ + [Given(@"the first number is (.*)")] + public void GivenTheFirstNumberIs(double firstNumber) + { + this.ScenarioContext.Set(firstNumber, "firstNumber"); + } + + [Given(@"the second number is (.*)")] + public void GivenTheSecondNumberIs(double secondNumber) + { + this.ScenarioContext.Set(secondNumber, "secondNumber"); + } + + [Then(@"the result should be (.*)")] + public void ThenTheResultShouldBe(int expected) + { + var actual = this.ScenarioContext.Get("actual"); + Assert.AreEqual(expected, actual); + } + + [When(@"the two numbers are added")] + public void WhenTheTwoNumbersAreAdded() + { + var firstNumber = this.ScenarioContext.Get("firstNumber"); + var secondNumber = this.ScenarioContext.Get("secondNumber"); + var calculation = new Calculation(); + var actual = calculation.Add(firstNumber, secondNumber); + this.ScenarioContext.Set(actual, "actual"); + } +} \ No newline at end of file From bf3993773050e7147e16fbff8b43ee6cb2f618cf Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 3 Jan 2022 23:02:48 +0800 Subject: [PATCH 125/267] refactor --- .../Lab.AllureReport4Specflow.csproj | 12 ++++----- ...0\250\210\347\256\227\346\251\237.feature" | 26 ++++++++++++++----- ...50\250\210\347\256\227\346\251\237Step.cs" | 16 ++++++------ 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj index 1c6c2cf4..3a6db6c1 100644 --- a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + + diff --git "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" index 9c952f95..4c4f7712 100644 --- "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" +++ "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237.feature" @@ -1,9 +1,21 @@ Feature: 計算機 - Simple calculator for adding two numbers +Simple calculator for adding two numbers -@mytag -Scenario: Add two numbers - Given the first number is 50 - And the second number is 70 - When the two numbers are added - Then the result should be 120 \ No newline at end of file + @mytag + Scenario: 相加兩個數字 + Given 第一個數字為 50 + And 第二個數字為 70 + When 兩個數字相加 + Then 結果應該為 120 + + Scenario Outline: 相加兩個數字(Examples) + Given 第一個數字為 + And 第二個數字為 + When 兩個數字相加 + Then 結果應該為 + + Examples: + | First | Second | Result | + | 50 | 70 | 120 | + | 30 | 40 | 70 | + | 60 | 30 | 90 | \ No newline at end of file diff --git "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" index e1ff71df..10ba428f 100644 --- "a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" +++ "b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/\350\250\210\347\256\227\346\251\237Step.cs" @@ -6,27 +6,27 @@ namespace Lab.AllureReport4Specflow; [Binding] public class 計算機Step : Steps { - [Given(@"the first number is (.*)")] - public void GivenTheFirstNumberIs(double firstNumber) + [Given(@"第一個數字為 (.*)")] + public void Given第一個數字為(double firstNumber) { this.ScenarioContext.Set(firstNumber, "firstNumber"); } - [Given(@"the second number is (.*)")] - public void GivenTheSecondNumberIs(double secondNumber) + [Given(@"第二個數字為 (.*)")] + public void Given第二個數字為(double secondNumber) { this.ScenarioContext.Set(secondNumber, "secondNumber"); } - [Then(@"the result should be (.*)")] - public void ThenTheResultShouldBe(int expected) + [Then(@"結果應該為 (.*)")] + public void Then結果應該為(double expected) { var actual = this.ScenarioContext.Get("actual"); Assert.AreEqual(expected, actual); } - [When(@"the two numbers are added")] - public void WhenTheTwoNumbersAreAdded() + [When(@"兩個數字相加")] + public void When兩個數字相加() { var firstNumber = this.ScenarioContext.Get("firstNumber"); var secondNumber = this.ScenarioContext.Get("secondNumber"); From d15093d54276b688d41745ba68ea771c82d9da71 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 4 Jan 2022 09:52:14 +0800 Subject: [PATCH 126/267] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20NUnit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.AllureReport4Specflow.csproj | 13 +++++----- .../Lab.AllureReport4Specflow/Tests.cs | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/Tests.cs diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj index 3a6db6c1..585d3b0a 100644 --- a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Lab.AllureReport4Specflow.csproj @@ -8,12 +8,13 @@ - - - - - - + + + + + + + diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Tests.cs b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Tests.cs new file mode 100644 index 00000000..5bfbd532 --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/Tests.cs @@ -0,0 +1,26 @@ +using Allure.Commons; +using NUnit.Allure.Attributes; +using NUnit.Allure.Core; +using NUnit.Framework; +using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert; + +namespace Lab.AllureReport4Specflow; + +[TestFixture] +[AllureNUnit] +[AllureSubSuite("Example")] +[AllureSeverity(SeverityLevel.critical)] +public class Tests +{ + [Test] + [AllureTag("NUnit","Debug")] + [AllureIssue("GitHub#1", "https://github.com/unickq/allure-nunit")] + [AllureFeature("Core")] + [TestCase(20, 50, 70)] + public void 相加兩個數字(double firstNumber, double secondNumber, double expected) + { + var calculation = new Calculation(); + var actual = calculation.Add(firstNumber, secondNumber); + Assert.AreEqual(expected, actual); + } +} \ No newline at end of file From 8f2ab0821ce18faa8a8a5097a3b6e7871e2debbd Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 6 Jan 2022 02:19:18 +0800 Subject: [PATCH 127/267] feat: add new ef core 6 project --- .../Lab.DynamoDB.SurveyTest.csproj | 18 +++ .../Lab.DynamoDB.SurveyTest/UnitTest1.cs | 138 ++++++++++++++++++ .../Lab.DynamoDB.SurveyTest/appsettings.json | 13 ++ DynamoDB/Lab.DynamoDB/Lab.DynamoDB.sln | 16 ++ DynamoDB/Lab.DynamoDB/docker-compose.yml | 16 ++ DynamoDB/Lab.DynamoDB/global.json | 7 + .../Lab.EFCoreBulk.UnitTest.csproj | 26 ++++ .../Lab.EFCoreBulk.UnitTest/MsTestHook.cs | 35 +++++ .../TestInstanceManager.cs | 34 +++++ .../Lab.EFCoreBulk.UnitTest/UnitTest1.cs | 32 ++++ ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln | 22 +++ .../AppDependencyInjectionExtensions.cs | 49 +++++++ .../Lab.EFCoreBulk/AppEnvironmentOption.cs | 36 +++++ .../Lab.EFCoreBulk/EntityModel/Employee.cs | 30 ++++ .../EntityModel/EmployeeDbContext.cs | 93 ++++++++++++ .../Lab.EFCoreBulk/EntityModel/Identity.cs | 33 +++++ .../EntityModel/OrderHistory.cs | 30 ++++ .../Lab.EFCoreBulk/EnvironmentAssistant.cs | 15 ++ .../Lab.EFCoreBulk/Lab.EFCoreBulk.csproj | 14 ++ ORM/EFCore/Lab.EFCoreBulk/docker-compose.yml | 10 ++ .../Lab.AllureReport4Specflow/.runsettings | 9 ++ .../Lab.AllureReport4Specflow/UnitTests.cs | 16 ++ 22 files changed, 692 insertions(+) create mode 100644 DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/Lab.DynamoDB.SurveyTest.csproj create mode 100644 DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/UnitTest1.cs create mode 100644 DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/appsettings.json create mode 100644 DynamoDB/Lab.DynamoDB/Lab.DynamoDB.sln create mode 100644 DynamoDB/Lab.DynamoDB/docker-compose.yml create mode 100644 DynamoDB/Lab.DynamoDB/global.json create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Employee.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Identity.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/OrderHistory.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EnvironmentAssistant.cs create mode 100644 ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/Lab.EFCoreBulk.csproj create mode 100644 ORM/EFCore/Lab.EFCoreBulk/docker-compose.yml create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/.runsettings create mode 100644 Test/Lab.AllureReport/Lab.AllureReport4Specflow/UnitTests.cs diff --git a/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/Lab.DynamoDB.SurveyTest.csproj b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/Lab.DynamoDB.SurveyTest.csproj new file mode 100644 index 00000000..efa12ca2 --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/Lab.DynamoDB.SurveyTest.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + enable + + false + + + + + + + + + + + diff --git a/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/UnitTest1.cs b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/UnitTest1.cs new file mode 100644 index 00000000..55041dc2 --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/UnitTest1.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void Survey_CreateTable() + { + var client = CreateAmazonDynamoDbClient(); + var request = new CreateTableRequest + { + AttributeDefinitions = new List() + { + new AttributeDefinition + { + AttributeName = "Id", + AttributeType = "N" + }, + new AttributeDefinition + { + AttributeName = "DateTime", + AttributeType = "S" + } + }, + + KeySchema = new List + { + new KeySchemaElement + { + AttributeName = "Id", + KeyType = "HASH" //Partition key + }, + new KeySchemaElement + { + AttributeName = "DateTime", + KeyType = "RANGE" //Range key + } + } + }; + var response = client.CreateTableAsync(request).Result; + } + + private static void CreateExampleTable(AmazonDynamoDBClient client, + string tableName, + CancellationToken cancel) + { + Console.WriteLine("\n*** Creating table ***"); + var request = new CreateTableRequest + { + AttributeDefinitions = new List() + { + new AttributeDefinition + { + AttributeName = "Id", + AttributeType = "N" + }, + new AttributeDefinition + { + AttributeName = "ReplyDateTime", + AttributeType = "N" + } + }, + KeySchema = new List + { + new KeySchemaElement + { + AttributeName = "Id", + KeyType = "HASH" //Partition key + }, + new KeySchemaElement + { + AttributeName = "ReplyDateTime", + KeyType = "RANGE" //Sort key + } + }, + ProvisionedThroughput = new ProvisionedThroughput + { + ReadCapacityUnits = 5, + WriteCapacityUnits = 6 + }, + TableName = tableName + }; + + var response = client.CreateTableAsync(request, cancel).Result; + + var tableDescription = response.TableDescription; + Console.WriteLine("{1}: {0} \t ReadsPerSec: {2} \t WritesPerSec: {3}", + tableDescription.TableStatus, + tableDescription.TableName, + tableDescription.ProvisionedThroughput.ReadCapacityUnits, + tableDescription.ProvisionedThroughput.WriteCapacityUnits); + + string status = tableDescription.TableStatus; + Console.WriteLine(tableName + " - " + status); + + WaitUntilTableReady(client, tableName,cancel); + } + private static void WaitUntilTableReady(AmazonDynamoDBClient client, string tableName,CancellationToken cancel) + { + string status = null; + // Let us wait until table is created. Call DescribeTable. + do + { + System.Threading.Thread.Sleep(5000); // Wait 5 seconds. + try + { + var res = client.DescribeTableAsync(new DescribeTableRequest + { + TableName = tableName + }, cancel).Result; + + Console.WriteLine("Table name: {0}, status: {1}", + res.Table.TableName, + res.Table.TableStatus); + status = res.Table.TableStatus; + } + catch (ResourceNotFoundException) + { + // DescribeTable is eventually consistent. So you might + // get resource not found. So we handle the potential exception. + } + } while (status != "ACTIVE"); + } + private static AmazonDynamoDBClient? CreateAmazonDynamoDbClient() + { + var clientConfig = new AmazonDynamoDBConfig + { + ServiceURL = "http://localhost:8000" + }; + var client = new AmazonDynamoDBClient(clientConfig); + return client; + } +} \ No newline at end of file diff --git a/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/appsettings.json b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/appsettings.json new file mode 100644 index 00000000..31ebdc71 --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.SurveyTest/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "DynamoDb": { + "LocalMode": true, + "LocalServiceUrl": "http://localhost:8000" + } +} \ No newline at end of file diff --git a/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.sln b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.sln new file mode 100644 index 00000000..e63b587d --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/Lab.DynamoDB.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DynamoDB.SurveyTest", "Lab.DynamoDB.SurveyTest\Lab.DynamoDB.SurveyTest.csproj", "{36FF7374-A6A2-436D-902D-8805336F3A38}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36FF7374-A6A2-436D-902D-8805336F3A38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36FF7374-A6A2-436D-902D-8805336F3A38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36FF7374-A6A2-436D-902D-8805336F3A38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36FF7374-A6A2-436D-902D-8805336F3A38}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/DynamoDB/Lab.DynamoDB/docker-compose.yml b/DynamoDB/Lab.DynamoDB/docker-compose.yml new file mode 100644 index 00000000..f8874b72 --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.8" + +services: + ddb: + image: amazon/dynamodb-local + command: ["-jar", "DynamoDBLocal.jar", "-inMemory", "-sharedDb"] + ports: + - 8000:8000 + ddb-admin: + image: aaronshaf/dynamodb-admin + environment: + - DYNAMO_ENDPOINT=http://ddb:8000 + ports: + - 8005:8001 + depends_on: + - ddb diff --git a/DynamoDB/Lab.DynamoDB/global.json b/DynamoDB/Lab.DynamoDB/global.json new file mode 100644 index 00000000..531745d4 --- /dev/null +++ b/DynamoDB/Lab.DynamoDB/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "3.1.100", + "rollForward": "latestMajor", + "allowPrerelease": false + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj new file mode 100644 index 00000000..d29b78a7 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..e14fc88d --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs @@ -0,0 +1,35 @@ +using Lab.EFCoreBulk.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.EFCoreBulk.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestInstanceManager.SetTestEnvironmentVariable(); + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestInstanceManager.SetTestEnvironmentVariable(); + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs new file mode 100644 index 00000000..7db399f1 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs @@ -0,0 +1,34 @@ +using System; +using Lab.EFCoreBulk.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.EFCoreBulk.UnitTest; + +internal class TestInstanceManager +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + static TestInstanceManager() + { + ConfigureTestServices(); + } + + public static void ConfigureTestServices() + { + var services = new ServiceCollection(); + services.AddAppEnvironment(); + services.AddEntityFramework(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True"; + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs new file mode 100644 index 00000000..b2524ef0 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs @@ -0,0 +1,32 @@ +using System; +using Lab.EFCoreBulk.EntityModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.EFCoreBulk.UnitTest; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod1() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var id = Guid.NewGuid(); + db.Employees.Add(new Employee + { + Id = id, + Age = 18, + CreateAt = DateTimeOffset.UtcNow, + Name = "yao", + CreateBy = "Sys", + // Identity = new Identity + // { + // Account = "yao", + // CreateAt = DateTimeOffset.UtcNow, + // CreateBy = "Sys", + // Password = "123456", + // }, + }); + db.SaveChanges(); + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln new file mode 100644 index 00000000..57f01a38 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EFCoreBulk", "Lab.EFCoreBulk\Lab.EFCoreBulk.csproj", "{3C9700D7-3563-4C7B-9EF4-A7EE512993DC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EFCoreBulk.UnitTest", "Lab.EFCoreBulk.UnitTest\Lab.EFCoreBulk.UnitTest.csproj", "{73344F9D-E263-4EE9-8193-F23343731E56}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3C9700D7-3563-4C7B-9EF4-A7EE512993DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C9700D7-3563-4C7B-9EF4-A7EE512993DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C9700D7-3563-4C7B-9EF4-A7EE512993DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C9700D7-3563-4C7B-9EF4-A7EE512993DC}.Release|Any CPU.Build.0 = Release|Any CPU + {73344F9D-E263-4EE9-8193-F23343731E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73344F9D-E263-4EE9-8193-F23343731E56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73344F9D-E263-4EE9-8193-F23343731E56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73344F9D-E263-4EE9-8193-F23343731E56}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..99b11430 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs @@ -0,0 +1,49 @@ +using Lab.EFCoreBulk.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.EFCoreBulk; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + ; + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs new file mode 100644 index 00000000..3a8f49cd --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs @@ -0,0 +1,36 @@ +namespace Lab.EFCoreBulk; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._memberDbConnectionString)) + { + this._memberDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._memberDbConnectionString; + } + set + { + this._memberDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _crmDbConnectionString; + private string _currentMarket; + private string _memberApiToken; + private string _memberDbConnectionString; + private string _memberServiceBaseEndpoint; + private string _webStoreDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Employee.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Employee.cs new file mode 100644 index 00000000..b2fec6b5 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.EFCoreBulk.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..2f2d5043 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,93 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.EFCoreBulk.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + + if (memoryOptions == null) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } + } + + s_migrated[0] = true; + } + } + } + + // 給 Migration CLI 使用 + // 建構函數配置失敗才需要以下處理 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // var connectionString = + // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; + // + // // var connectionString = this._connectionString; + // if (optionsBuilder.IsConfigured == false) + // { + // optionsBuilder.UseSqlServer(connectionString); + // } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + + modelBuilder.Entity(p => + { + }); + } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Identity.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Identity.cs new file mode 100644 index 00000000..47575538 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.EFCoreBulk.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/OrderHistory.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..c1de6f7b --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.EFCoreBulk.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EnvironmentAssistant.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EnvironmentAssistant.cs new file mode 100644 index 00000000..942a8c40 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.EFCoreBulk; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/Lab.EFCoreBulk.csproj b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/Lab.EFCoreBulk.csproj new file mode 100644 index 00000000..39ae089b --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/Lab.EFCoreBulk.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/ORM/EFCore/Lab.EFCoreBulk/docker-compose.yml b/ORM/EFCore/Lab.EFCoreBulk/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/ORM/EFCore/Lab.EFCoreBulk/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/.runsettings b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/.runsettings new file mode 100644 index 00000000..2f5e2ef5 --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/.runsettings @@ -0,0 +1,9 @@ + + + + .\TestResults + + trx + + + diff --git a/Test/Lab.AllureReport/Lab.AllureReport4Specflow/UnitTests.cs b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/UnitTests.cs new file mode 100644 index 00000000..7e06cfee --- /dev/null +++ b/Test/Lab.AllureReport/Lab.AllureReport4Specflow/UnitTests.cs @@ -0,0 +1,16 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AllureReport4Specflow; + +[TestClass] +public class UnitTests +{ + [TestMethod] + [DataRow(50,70,120)] + public void 相加兩個數字(double firstNumber, double secondNumber, double expected) + { + var calculation = new Calculation(); + var actual = calculation.Add(firstNumber, secondNumber); + Assert.AreEqual(expected, actual); + } +} \ No newline at end of file From f676365471204ea072a789156c2a0c52cf713519 Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 7 Jan 2022 02:26:20 +0800 Subject: [PATCH 128/267] feat: add test case --- .../Lab.EFCoreBulk.UnitTest.csproj | 21 ++-- .../TestInstanceManager.cs | 8 +- .../Lab.EFCoreBulk.UnitTest/UnitTest1.cs | 104 ++++++++++++++++++ .../Lab.EFCoreBulk/AppEnvironmentOption.cs | 15 +-- 4 files changed, 124 insertions(+), 24 deletions(-) diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj index d29b78a7..07ee4013 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/Lab.EFCoreBulk.UnitTest.csproj @@ -8,19 +8,20 @@ - - - - - - - - - + + + + + + + + + + - + diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs index 7db399f1..8d6ac967 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/TestInstanceManager.cs @@ -14,12 +14,12 @@ internal class TestInstanceManager static TestInstanceManager() { - ConfigureTestServices(); + var services = new ServiceCollection(); + ConfigureTestServices(services); } - public static void ConfigureTestServices() + public static void ConfigureTestServices(IServiceCollection services) { - var services = new ServiceCollection(); services.AddAppEnvironment(); services.AddEntityFramework(); _serviceProvider = services.BuildServiceProvider(); @@ -29,6 +29,6 @@ public static void SetTestEnvironmentVariable() { var option = _serviceProvider.GetService(); option.EmployeeDbConnectionString = - "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True"; + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs index b2524ef0..88d0b7b0 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs @@ -1,12 +1,99 @@ using System; +using System.Diagnostics; +using System.Linq; +using EFCore.BulkExtensions; using Lab.EFCoreBulk.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.EFCoreBulk.UnitTest; + [TestClass] public class UnitTest1 { + [TestMethod] + public void AddRanges() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var totalCount = 1000000; + var toDb = Enumerable.Range(0, totalCount) + .Select(x => new Employee + { + Id = Guid.NewGuid(), + + // Id = Guid.NewGuid(), + Age = 10, + + // Age = RandomNumber.Next(1, 100), + CreateBy = "yao", + + // CreateBy = Name.FullName(), + CreateAt = DateTimeOffset.Now, + + // CreateAt = DateTimeOffset.Now, + Name = "yao" + + // Name = Name.First(), + }).ToList(); + + var watch = new Stopwatch(); + watch.Restart(); + + db.AddRange(toDb); + + // db.BulkInsert(employees); + + watch.Stop(); + + var count = db.Employees.Count(); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + } + + [TestMethod] + public void BulkInsert() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var totalCount = 1000000; + var employees = Enumerable.Range(0, totalCount) + .Select(x => new Employee + { + Id = Guid.NewGuid(), + + // Id = Guid.NewGuid(), + Age = 10, + + // Age = RandomNumber.Next(1, 100), + CreateBy = "yao", + + // CreateBy = Name.FullName(), + CreateAt = DateTimeOffset.Now, + + // CreateAt = DateTimeOffset.Now, + Name = "yao" + + // Name = Name.First(), + }).ToList(); + + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; + var watch = new Stopwatch(); + watch.Restart(); + + db.BulkInsert(employees, config); + + // db.BulkInsert(employees); + + watch.Stop(); + + var count = db.Employees.Count(); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + } + + + + [TestMethod] public void TestMethod1() { @@ -19,6 +106,7 @@ public void TestMethod1() CreateAt = DateTimeOffset.UtcNow, Name = "yao", CreateBy = "Sys", + // Identity = new Identity // { // Account = "yao", @@ -29,4 +117,20 @@ public void TestMethod1() }); db.SaveChanges(); } + + [TestMethod] + public void TestMethod2() + { + var host = CreateHostBuilder(null).Start(); + host.Services.GetService>(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureServices((hostBuilder, services) => + { + TestInstanceManager.ConfigureTestServices(services); + }); + } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs index 3a8f49cd..ef209511 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppEnvironmentOption.cs @@ -6,27 +6,22 @@ public string EmployeeDbConnectionString { get { - if (string.IsNullOrWhiteSpace(this._memberDbConnectionString)) + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) { - this._memberDbConnectionString = + this._employeeDbConnectionString = EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); } - return this._memberDbConnectionString; + return this._employeeDbConnectionString; } set { - this._memberDbConnectionString = value; + this._employeeDbConnectionString = value; Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); } } - private string _crmDbConnectionString; - private string _currentMarket; - private string _memberApiToken; - private string _memberDbConnectionString; - private string _memberServiceBaseEndpoint; - private string _webStoreDbConnectionString; + private string _employeeDbConnectionString; private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; public void Initial() From 2340a89b235abfee5ca8f037438c11fb01fd00f2 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 9 Jan 2022 17:48:25 +0800 Subject: [PATCH 129/267] feat: add test case --- .../Lab.EFCoreBulk.UnitTest/MsTestHook.cs | 6 +- .../Lab.EFCoreBulk.UnitTest/UnitTest1.cs | 193 ++++++++++++++---- .../AppDependencyInjectionExtensions.cs | 5 +- .../EntityModel/EmployeeDbContext.cs | 18 -- 4 files changed, 157 insertions(+), 65 deletions(-) diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs index e14fc88d..481072e2 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/MsTestHook.cs @@ -1,8 +1,4 @@ -using Lab.EFCoreBulk.EntityModel; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.EFCoreBulk.UnitTest; diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs index 88d0b7b0..85d50674 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using EFCore.BulkExtensions; @@ -10,7 +11,6 @@ namespace Lab.EFCoreBulk.UnitTest; - [TestClass] public class UnitTest1 { @@ -18,33 +18,43 @@ public class UnitTest1 public void AddRanges() { var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); - var totalCount = 1000000; - var toDb = Enumerable.Range(0, totalCount) - .Select(x => new Employee - { - Id = Guid.NewGuid(), - - // Id = Guid.NewGuid(), - Age = 10, + var toDb = GetEmployees(1000000); + var watch = new Stopwatch(); + watch.Restart(); - // Age = RandomNumber.Next(1, 100), - CreateBy = "yao", + db.AddRange(toDb); + var changeCount = db.SaveChanges(); - // CreateBy = Name.FullName(), - CreateAt = DateTimeOffset.Now, + watch.Stop(); - // CreateAt = DateTimeOffset.Now, - Name = "yao" + var count = db.Employees.Count(); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + } - // Name = Name.First(), - }).ToList(); + [TestMethod] + public void BatchUpdate() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var toDb = GetEmployees(10000); + var update = new Employee + { + Id = Guid.NewGuid(), + Age = 10, + CreateBy = "yao", + CreateAt = DateTimeOffset.Now, + Name = "yao", + Remark = "等待更新" + }; + toDb.Add(update); + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; + db.BulkInsert(toDb, config); var watch = new Stopwatch(); watch.Restart(); - db.AddRange(toDb); - - // db.BulkInsert(employees); + db.Employees + .Where(p => p.Id == update.Id) + .BatchUpdate(new Employee() { Remark = "Updated" }); watch.Stop(); @@ -56,34 +66,68 @@ public void AddRanges() public void BulkInsert() { var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); - var totalCount = 1000000; - var employees = Enumerable.Range(0, totalCount) - .Select(x => new Employee - { - Id = Guid.NewGuid(), + var toDb = GetEmployees(1000000); - // Id = Guid.NewGuid(), - Age = 10, + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; - // Age = RandomNumber.Next(1, 100), - CreateBy = "yao", + var watch = new Stopwatch(); + watch.Restart(); - // CreateBy = Name.FullName(), - CreateAt = DateTimeOffset.Now, + db.BulkInsert(toDb, config); - // CreateAt = DateTimeOffset.Now, - Name = "yao" + watch.Stop(); - // Name = Name.First(), - }).ToList(); + var count = db.Employees.Count(); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + } + + [TestMethod] + public void BulkReadAsync() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var toDb = GetEmployees(100000); + + db.AddRange(toDb); + + var config = new BulkConfig + { + PropertiesToExclude = new List { "SequenceId" }, + SetOutputIdentity = false, + BatchSize = 4000, + UseTempDB = true + }; - var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; var watch = new Stopwatch(); watch.Restart(); - db.BulkInsert(employees, config); + db.BulkRead(new List { "yao" }); + + watch.Stop(); + + var count = db.Employees.Count(); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + } - // db.BulkInsert(employees); + [TestMethod] + public void BulkSaveChanges() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var toDb = GetEmployees(1000); + + db.AddRange(toDb); + + var config = new BulkConfig + { + PropertiesToExclude = new List { "SequenceId" }, + BulkCopyTimeout = 30, + BatchSize = 4000, + UseTempDB = true + }; + + var watch = new Stopwatch(); + watch.Restart(); + + db.BulkSaveChanges(config); watch.Stop(); @@ -91,9 +135,18 @@ public void BulkInsert() Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); } - - - + [TestCleanup] + public void TestCleanup() + { + CleanData(); + } + + [TestInitialize] + public void TestInitialize() + { + CleanData(); + } + [TestMethod] public void TestMethod1() { @@ -125,6 +178,40 @@ public void TestMethod2() host.Services.GetService>(); } + private static void CleanData() + { + using var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + // db.Truncate(); + // db.Truncate(); + using var transaction = db.Database.BeginTransaction(); + + db.OrderHistories + .BatchDelete(); + + db.Identities + .BatchDelete(); + + // db.Truncate(); + db.Employees + .BatchDelete(); + + transaction.Commit(); + + // db.Employees + // .Where(p => p.Id != Guid.Empty) + // .BatchDelete(); + // + // while (db.Employees.Any()) + // { + // var deletedCount = db.Employees + // .Where(p => p.Id != Guid.Empty) + // .Take(1000000) + // .BatchDelete(); + // var count = db.Employees.Count(); + // Console.WriteLine($"已刪除 {deletedCount} 筆,剩下 {count} 筆"); + // } + } + private static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) @@ -133,4 +220,28 @@ private static IHostBuilder CreateHostBuilder(string[] args) TestInstanceManager.ConfigureTestServices(services); }); } + + private static List GetEmployees(int totalCount) + { + var employees = Enumerable.Range(0, totalCount) + .Select(x => new Employee + { + Id = Guid.NewGuid(), + + // Id = Guid.NewGuid(), + Age = 10, + + // Age = RandomNumber.Next(1, 100), + CreateBy = "yao", + + // CreateBy = Name.FullName(), + CreateAt = DateTimeOffset.Now, + + // CreateAt = DateTimeOffset.Now, + Name = "yao" + + // Name = Name.First(), + }).ToList(); + return employees; + } } \ No newline at end of file diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs index 99b11430..3a0becd7 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/AppDependencyInjectionExtensions.cs @@ -9,7 +9,10 @@ public static class AppDependencyInjectionExtensions { public static void AddAppEnvironment(this IServiceCollection services) { - services.AddLogging(); + services.AddLogging(builder => + { + builder.AddConsole(); + }); services.AddSingleton(); } diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs index 2f2d5043..e34a7e7b 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk/EntityModel/EmployeeDbContext.cs @@ -44,20 +44,6 @@ public EmployeeDbContext(DbContextOptions options) } } - // 給 Migration CLI 使用 - // 建構函數配置失敗才需要以下處理 - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - // var connectionString = - // "Server=(localdb)\\mssqllocaldb;Database=Lab.DAL.UnitTest;Trusted_Connection=True;MultipleActiveResultSets=true"; - // - // // var connectionString = this._connectionString; - // if (optionsBuilder.IsConfigured == false) - // { - // optionsBuilder.UseSqlServer(connectionString); - // } - } - //管理索引 protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -84,10 +70,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsUnique() .IsClustered(); }); - - modelBuilder.Entity(p => - { - }); } } } \ No newline at end of file From 39f3b07024a58a4346d37d05f4d0567168830344 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 10 Jan 2022 02:07:02 +0800 Subject: [PATCH 130/267] feat: add test case --- .../Lab.EFCoreBulk.UnitTest/UnitTest1.cs | 102 ++++++++++++++---- 1 file changed, 82 insertions(+), 20 deletions(-) diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs index 85d50674..5ecaf99f 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.UnitTest/UnitTest1.cs @@ -31,6 +31,39 @@ public void AddRanges() Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); } + [TestMethod] + public void BatchDelete() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var toDb = GetEmployees(10000); + var update = new Employee + { + Id = Guid.NewGuid(), + Age = 10, + CreateBy = "yao", + CreateAt = DateTimeOffset.Now, + Name = "yao", + Remark = "等待更新" + }; + toDb.Add(update); + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; + db.BulkInsert(toDb, config); + + var watch = new Stopwatch(); + watch.Restart(); + + db.Employees + .Where(p => p.Id == update.Id) + .BatchDelete(); + + watch.Stop(); + + var count = db.Employees.Count(); + var isExist = db.Employees.Any(p => p.Id == update.Id); + Assert.AreEqual(false, isExist); + Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed},{update.Id} 資料不存在"); + } + [TestMethod] public void BatchUpdate() { @@ -54,7 +87,7 @@ public void BatchUpdate() db.Employees .Where(p => p.Id == update.Id) - .BatchUpdate(new Employee() { Remark = "Updated" }); + .BatchUpdate(new Employee { Remark = "Updated" }); watch.Stop(); @@ -82,30 +115,37 @@ public void BulkInsert() } [TestMethod] - public void BulkReadAsync() + public void BulkRead() { var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); - var toDb = GetEmployees(100000); - - db.AddRange(toDb); - - var config = new BulkConfig + var toDb = GetEmployees(100); { - PropertiesToExclude = new List { "SequenceId" }, - SetOutputIdentity = false, - BatchSize = 4000, - UseTempDB = true - }; + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; + db.BulkInsert(toDb, config); + } var watch = new Stopwatch(); watch.Restart(); - - db.BulkRead(new List { "yao" }); + { + var items = new List + { + new() { Name = "yao1" }, + new() { Name = "yao2" } + }; + var config = new BulkConfig + { + UpdateByProperties = new List + { + nameof(Employee.Name), + }, + UseTempDB = true + }; + db.BulkRead(items, config); + } watch.Stop(); - var count = db.Employees.Count(); - Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); + Console.WriteLine($"共花費={watch.Elapsed}"); } [TestMethod] @@ -135,6 +175,27 @@ public void BulkSaveChanges() Console.WriteLine($"資料庫存在筆數={count},共花費={watch.Elapsed}"); } + [TestMethod] + public void Contains() + { + var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + var toDb = GetEmployees(100); + { + var config = new BulkConfig { SetOutputIdentity = false, BatchSize = 4000, UseTempDB = true }; + db.BulkInsert(toDb, config); + } + + var watch = new Stopwatch(); + watch.Restart(); + + var items = new List { "yao1", "yao2" }; + var employees = db.Employees.Where(a => items.Contains(a.Name)).AsNoTracking().ToList(); //SQL IN operator + + watch.Stop(); + + Console.WriteLine($"共花費={watch.Elapsed}"); + } + [TestCleanup] public void TestCleanup() { @@ -181,10 +242,11 @@ public void TestMethod2() private static void CleanData() { using var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); + // db.Truncate(); // db.Truncate(); using var transaction = db.Database.BeginTransaction(); - + db.OrderHistories .BatchDelete(); @@ -194,7 +256,7 @@ private static void CleanData() // db.Truncate(); db.Employees .BatchDelete(); - + transaction.Commit(); // db.Employees @@ -224,7 +286,7 @@ private static IHostBuilder CreateHostBuilder(string[] args) private static List GetEmployees(int totalCount) { var employees = Enumerable.Range(0, totalCount) - .Select(x => new Employee + .Select((x, i) => new Employee { Id = Guid.NewGuid(), @@ -238,7 +300,7 @@ private static List GetEmployees(int totalCount) CreateAt = DateTimeOffset.Now, // CreateAt = DateTimeOffset.Now, - Name = "yao" + Name = $"yao{i}" // Name = Name.First(), }).ToList(); From df03033c75b2d125dad3354fbfcc9637cf0e2d5e Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 10:26:34 +0800 Subject: [PATCH 131/267] feat: add mimiprofiler project --- .../Controllers/ValuesController.cs | 82 +++++++++++++++ .../Controllers/WeatherForecastController.cs | 34 +++++++ .../Lab.NETMiniProfiler.ASPNetCore5.csproj | 21 ++++ .../Program.cs | 16 +++ .../Properties/launchSettings.json | 31 ++++++ .../Startup.cs | 62 ++++++++++++ .../WeatherForecast.cs | 13 +++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 ++ .../index.html | 99 +++++++++++++++++++ .../Lab.NETMiniProfiler.sln | 25 +++++ 11 files changed, 400 insertions(+) create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Program.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.Development.json create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.json create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/index.html create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs new file mode 100644 index 00000000..0a1d0d4e --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs @@ -0,0 +1,82 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; +using StackExchange.Profiling; + +namespace Lab.NETMiniProfiler.ASPNetCore5.Controllers +{ + /// + /// Value Controller + /// + [Route("[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + /// + /// Get Api + /// + /// + // GET api/values + [HttpGet] + public ActionResult> Get() + { + string url1 = string.Empty; + string url2 = string.Empty; + using (MiniProfiler.Current.Step("Get方法")) + { + using (MiniProfiler.Current.Step("准备数据")) + { + using (MiniProfiler.Current.CustomTiming("SQL", "SELECT * FROM Config")) + { + // 模拟一个SQL查询 + Thread.Sleep(500); + + url1 = "https://www.baidu.com"; + url2 = "https://www.sina.com.cn/"; + } + } + + + using (MiniProfiler.Current.Step("使用从数据库中查询的数据,进行Http请求")) + { + using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url1)) + { + var client = new WebClient(); + var reply = client.DownloadString(url1); + } + + using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url2)) + { + var client = new WebClient(); + var reply = client.DownloadString(url2); + } + } + } + return new string[] { "value1", "value2" }; + } + + // GET api/values/5 + [HttpGet("{id}")] + public ActionResult Get(int id) + { + return "value"; + } + + // POST api/values + [HttpPost] + public void Post([FromBody] string value) + { + } + + // PUT api/values/5 + [HttpPut("{id}")] + public void Put(int id, [FromBody] string value) + { + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..f1a485f1 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.NETMiniProfiler.ASPNetCore5.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj new file mode 100644 index 00000000..8e212a10 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + enable + enable + 10 + + + + + + + + + + + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Program.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Program.cs new file mode 100644 index 00000000..0d2a45b3 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Program.cs @@ -0,0 +1,16 @@ +namespace Lab.NETMiniProfiler.ASPNetCore5 +{ + public class Program + { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } + + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json new file mode 100644 index 00000000..2ac1d82f --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:41185", + "sslPort": 44361 + } + }, + "profiles": { + "Lab.NETMiniProfiler.ASPNetCore5": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7186;http://localhost:5186", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs new file mode 100644 index 00000000..74aafb55 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using Microsoft.OpenApi.Models; + +namespace Lab.NETMiniProfiler.ASPNetCore5 +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + this.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.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); + }); + + services.AddMiniProfiler(options => + options.RouteBasePath = "/profiler" + ); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseMiniProfiler(); + + app.UseSwagger(); + //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); + app.UseSwaggerUI(c => + { + c.RoutePrefix = "swagger"; + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + c.IndexStream = () => this.GetType() + .GetTypeInfo() + .Assembly + .GetManifestResourceStream("Lab.NETMiniProfiler.ASPNetCore5.index.html"); + }); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs new file mode 100644 index 00000000..de49d97c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace Lab.NETMiniProfiler.ASPNetCore5 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.Development.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/index.html b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/index.html new file mode 100644 index 00000000..49c7aa3d --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/index.html @@ -0,0 +1,99 @@ + + + + + + + + %(DocumentTitle) + + + + + + + %(HeadContent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln new file mode 100644 index 00000000..40be8a71 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31911.196 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab.NETMiniProfiler.ASPNetCore5", "Lab.NETMiniProfiler.ASPNetCore5\Lab.NETMiniProfiler.ASPNetCore5.csproj", "{8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1A39E53A-25EB-4546-9E76-DA1904FE5DCA} + EndGlobalSection +EndGlobal From 4238a4c69c1b200ad52b4360fc07d6a8a8037ad7 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 10:45:20 +0800 Subject: [PATCH 132/267] add files --- .../Lab.NETMiniProfiler.ASPNetCore5.csproj | 4 + .../Properties/launchSettings.json | 3 + .../Startup.cs | 3 + .../AppDependencyInjectionExtensions.cs | 52 ++++++++++ .../AppEnvironmentOption.cs | 31 ++++++ .../EntityModel/Employee.cs | 30 ++++++ .../EntityModel/EmployeeDbContext.cs | 69 +++++++++++++ .../EntityModel/Identity.cs | 33 +++++++ .../EntityModel/OrderHistory.cs | 30 ++++++ .../EnvironmentAssistant.cs | 15 +++ ...MiniProfiler.Infrastructure.EFCore5.csproj | 17 ++++ .../AppDependencyInjectionExtensions.cs | 52 ++++++++++ .../AppEnvironmentOption.cs | 31 ++++++ .../EntityModel/Employee.cs | 30 ++++++ .../EntityModel/EmployeeDbContext.cs | 75 ++++++++++++++ .../EntityModel/Identity.cs | 33 +++++++ .../EntityModel/OrderHistory.cs | 30 ++++++ .../EnvironmentAssistant.cs | 15 +++ ...MiniProfiler.Infrastructure.EFCore6.csproj | 15 +++ .../Lab.NETMiniProfiler.sln | 6 ++ .../Controllers/ValueController.cs | 54 ++++++++++ .../Controllers/WeatherForecastController.cs | 39 ++++++++ .../WebApplication1/Program.cs | 26 +++++ .../Properties/launchSettings.json | 31 ++++++ .../WebApplication1/Startup.cs | 74 ++++++++++++++ .../WebApplication1/WeatherForecast.cs | 15 +++ .../WebApplication1/WebApplication1.csproj | 21 ++++ .../appsettings.Development.json | 9 ++ .../WebApplication1/appsettings.json | 10 ++ .../WebApplication1/index.html | 99 +++++++++++++++++++ 30 files changed, 952 insertions(+) create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Employee.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Identity.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/OrderHistory.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EnvironmentAssistant.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json create mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj index 8e212a10..44130051 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj @@ -18,4 +18,8 @@
+ + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json index 2ac1d82f..be2ddc83 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json @@ -6,6 +6,9 @@ "iisExpress": { "applicationUrl": "http://localhost:41185", "sslPort": 44361 + }, + "environmentVariables": { + "EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" } }, "profiles": { diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs index 74aafb55..46794e32 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Lab.NETMiniProfiler.Infrastructure.EFCore5; using Microsoft.OpenApi.Models; namespace Lab.NETMiniProfiler.ASPNetCore5 @@ -24,6 +25,8 @@ public void ConfigureServices(IServiceCollection services) services.AddMiniProfiler(options => options.RouteBasePath = "/profiler" ); + services.AddAppEnvironment(); + services.AddEntityFramework(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..22a801f0 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs @@ -0,0 +1,52 @@ +using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => + { + builder.AddConsole(); + }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + ; + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs new file mode 100644 index 00000000..dcc2f14d --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs @@ -0,0 +1,31 @@ +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _employeeDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Employee.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Employee.cs new file mode 100644 index 00000000..51384037 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..8de5bc83 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } + + s_migrated[0] = true; + } + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Identity.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Identity.cs new file mode 100644 index 00000000..8537c235 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/OrderHistory.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..4da48bbe --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EnvironmentAssistant.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EnvironmentAssistant.cs new file mode 100644 index 00000000..32256cff --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj new file mode 100644 index 00000000..d2a6aa4b --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj @@ -0,0 +1,17 @@ + + + + net5.0 + enable + enable + 10 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..094946b8 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs @@ -0,0 +1,52 @@ +using Lab.NETMiniProfiler.Infrastructure.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.NETMiniProfiler.Infrastructure; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => + { + builder.AddConsole(); + }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + ; + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs new file mode 100644 index 00000000..42da1ef1 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs @@ -0,0 +1,31 @@ +namespace Lab.NETMiniProfiler.Infrastructure; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _employeeDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs new file mode 100644 index 00000000..cf11b247 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..f07f9b6b --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,75 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + + if (memoryOptions == null) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } + } + + s_migrated[0] = true; + } + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs new file mode 100644 index 00000000..57b2a437 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..63c19ce4 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs new file mode 100644 index 00000000..7f2ff85c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.NETMiniProfiler.Infrastructure; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj new file mode 100644 index 00000000..c5a492b3 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + Lab.NETMiniProfiler.Infrastructure.EFCore6 + + + + + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln index 40be8a71..2cae23eb 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.31911.196 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab.NETMiniProfiler.ASPNetCore5", "Lab.NETMiniProfiler.ASPNetCore5\Lab.NETMiniProfiler.ASPNetCore5.csproj", "{8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.NETMiniProfiler.Infrastructure.EFCore5", "Lab.NETMiniProfiler.Infrastructure.EFCore5\Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj", "{083D436C-B451-4BCE-8A97-E6E77B9F9A23}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04}.Release|Any CPU.Build.0 = Release|Any CPU + {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs new file mode 100644 index 00000000..7ff8d74c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Mvc; +using StackExchange.Profiling; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace WebApplication1.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ValueController : ControllerBase + { + [HttpGet] + public IEnumerable Get() + { + string url1 = string.Empty; + string url2 = string.Empty; + using (MiniProfiler.Current.Step("Get方法")) + { + using (MiniProfiler.Current.Step("准备数据")) + { + using (MiniProfiler.Current.CustomTiming("SQL", "SELECT * FROM Config")) + { + // 模拟一个SQL查询 + Thread.Sleep(500); + + url1 = "https://www.baidu.com"; + url2 = "https://www.sina.com.cn/"; + } + } + + + using (MiniProfiler.Current.Step("使用从数据库中查询的数据,进行Http请求")) + { + using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url1)) + { + var client = new WebClient(); + var reply = client.DownloadString(url1); + } + + using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url2)) + { + var client = new WebClient(); + var reply = client.DownloadString(url2); + } + } + } + return new string[] { "value1", "value2" }; + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..47134b11 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication1.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs new file mode 100644 index 00000000..53f3427e --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication1 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json new file mode 100644 index 00000000..1496716e --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64791", + "sslPort": 44397 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApplication1": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs new file mode 100644 index 00000000..10a9665d --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs @@ -0,0 +1,74 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace WebApplication1 +{ + 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.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); + }); + + services.AddMiniProfiler(options => + options.RouteBasePath = "/profiler" + ); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseMiniProfiler(); + + app.UseSwagger(); + //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); + app.UseSwaggerUI(c => + { + c.RoutePrefix = "swagger"; + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + c.IndexStream = () => GetType() + .GetTypeInfo() + .Assembly + .GetManifestResourceStream("WebApplication1.index.html"); + }); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs new file mode 100644 index 00000000..11a0296b --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace WebApplication1 +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj new file mode 100644 index 00000000..cd66119c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + + + + + + + + + + + + + + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html b/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html new file mode 100644 index 00000000..49c7aa3d --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html @@ -0,0 +1,99 @@ + + + + + + + + %(DocumentTitle) + + + + + + + %(HeadContent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + From 3c5627a8c67caff7d24d7f4c5c6971cd74a3d990 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 11:24:29 +0800 Subject: [PATCH 133/267] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E7=B5=90=E6=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln index 57f01a38..592822e0 100644 --- a/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln +++ b/ORM/EFCore/Lab.EFCoreBulk/Lab.EFCoreBulk.sln @@ -4,6 +4,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EFCoreBulk", "Lab.EFCor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EFCoreBulk.UnitTest", "Lab.EFCoreBulk.UnitTest\Lab.EFCoreBulk.UnitTest.csproj", "{73344F9D-E263-4EE9-8193-F23343731E56}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3B774B51-B4E4-4F3F-9E1F-43E4A620245A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,4 +26,8 @@ Global {73344F9D-E263-4EE9-8193-F23343731E56}.Release|Any CPU.ActiveCfg = Release|Any CPU {73344F9D-E263-4EE9-8193-F23343731E56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3C9700D7-3563-4C7B-9EF4-A7EE512993DC} = {3B774B51-B4E4-4F3F-9E1F-43E4A620245A} + {73344F9D-E263-4EE9-8193-F23343731E56} = {3B774B51-B4E4-4F3F-9E1F-43E4A620245A} + EndGlobalSection EndGlobal From 4ccffec9a546cfb258d9f5931ee6a99581c55b9b Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 12:22:11 +0800 Subject: [PATCH 134/267] add get/post action --- .../Controllers/ValuesController.cs | 83 +++++++++---------- .../Lab.NETMiniProfiler.ASPNetCore5.csproj | 1 + .../Properties/launchSettings.json | 9 +- .../Startup.cs | 58 ++++++++----- .../AppEnvironmentOption.cs | 6 +- .../EntityModel/EmployeeDbContext.cs | 17 ++-- ...MiniProfiler.Infrastructure.EFCore5.csproj | 2 +- .../Lab.NETMiniProfiler.sln | 11 +++ .../Lab.NETMiniProfiler/docker-compose.yml | 10 +++ 9 files changed, 116 insertions(+), 81 deletions(-) create mode 100644 Benchmark/Lab.NETMiniProfiler/docker-compose.yml diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs index 0a1d0d4e..811a23c0 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs @@ -1,57 +1,44 @@ -using System.Net; +using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using StackExchange.Profiling; namespace Lab.NETMiniProfiler.ASPNetCore5.Controllers { /// - /// Value Controller + /// Value Controller /// [Route("[controller]")] [ApiController] public class ValuesController : ControllerBase { + private readonly IDbContextFactory _employeeDbContextFactory; + + public ValuesController(IDbContextFactory employeeDbContextFactory) + { + this._employeeDbContextFactory = employeeDbContextFactory; + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + /// - /// Get Api + /// Get Api /// /// + // GET api/values [HttpGet] - public ActionResult> Get() + public async Task Get(CancellationToken cancel = default) { - string url1 = string.Empty; - string url2 = string.Empty; - using (MiniProfiler.Current.Step("Get方法")) + using (MiniProfiler.Current.Step("查詢資料庫")) { - using (MiniProfiler.Current.Step("准备数据")) - { - using (MiniProfiler.Current.CustomTiming("SQL", "SELECT * FROM Config")) - { - // 模拟一个SQL查询 - Thread.Sleep(500); - - url1 = "https://www.baidu.com"; - url2 = "https://www.sina.com.cn/"; - } - } - - - using (MiniProfiler.Current.Step("使用从数据库中查询的数据,进行Http请求")) - { - using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url1)) - { - var client = new WebClient(); - var reply = client.DownloadString(url1); - } - - using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url2)) - { - var client = new WebClient(); - var reply = client.DownloadString(url2); - } - } + await using var db = this._employeeDbContextFactory.CreateDbContext(); + return this.Ok(await db.Employees.AsTracking().ToListAsync(cancel)); } - return new string[] { "value1", "value2" }; } // GET api/values/5 @@ -63,8 +50,24 @@ public ActionResult Get(int id) // POST api/values [HttpPost] - public void Post([FromBody] string value) + public async Task Post(CancellationToken cancellationToken = default) { + using (MiniProfiler.Current.Step("異動資料庫")) + { + await using var db = this._employeeDbContextFactory.CreateDbContext(); + + var toDb = new Employee + { + Id = Guid.NewGuid(), + CreateAt = DateTimeOffset.Now, + CreateBy = Faker.Name.FullName(), + Age = Faker.RandomNumber.Next(1, 100), + Name = Faker.Name.Suffix(), + }; + db.Employees.Add(toDb); + await db.SaveChangesAsync(cancellationToken); + return this.Ok(toDb); + } } // PUT api/values/5 @@ -72,11 +75,5 @@ public void Post([FromBody] string value) public void Put(int id, [FromBody] string value) { } - - // DELETE api/values/5 - [HttpDelete("{id}")] - public void Delete(int id) - { - } } -} +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj index 44130051..98d92d65 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj @@ -8,6 +8,7 @@ + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json index be2ddc83..05ed2abd 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json @@ -7,9 +7,6 @@ "applicationUrl": "http://localhost:41185", "sslPort": 44361 }, - "environmentVariables": { - "EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" - } }, "profiles": { "Lab.NETMiniProfiler.ASPNetCore5": { @@ -19,7 +16,8 @@ "launchUrl": "swagger", "applicationUrl": "https://localhost:7186;http://localhost:5186", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" } }, "IIS Express": { @@ -27,7 +25,8 @@ "launchBrowser": true, "launchUrl": "swagger", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" } } } diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs index 46794e32..fd257d87 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs @@ -1,32 +1,19 @@ +using System.Diagnostics; using System.Reflection; using Lab.NETMiniProfiler.Infrastructure.EFCore5; +using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel; +using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; namespace Lab.NETMiniProfiler.ASPNetCore5 { public class Startup { - public Startup(IConfiguration configuration) - { - this.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) + public Startup(IConfiguration configuration) { - services.AddControllers(); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); - }); - - services.AddMiniProfiler(options => - options.RouteBasePath = "/profiler" - ); - services.AddAppEnvironment(); - services.AddEntityFramework(); + this.Configuration = configuration; } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -35,9 +22,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - app.UseMiniProfiler(); app.UseSwagger(); + //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); app.UseSwaggerUI(c => { @@ -48,18 +35,45 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) .Assembly .GetManifestResourceStream("Lab.NETMiniProfiler.ASPNetCore5.index.html"); }); + + app.UseMiniProfiler(); } + VerifyDbConnection(app); + app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); - app.UseEndpoints(endpoints => + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSwaggerGen(c => { - endpoints.MapControllers(); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); }); + + services.AddMiniProfiler(o => o.RouteBasePath = "/profiler") + .AddEntityFramework(); + services.AddAppEnvironment(); + services.AddEntityFramework(); + } + + private static void VerifyDbConnection(IApplicationBuilder app) + { + var employeeDbContextFactory = + app.ApplicationServices.GetService>(); + var db = employeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + Debug.WriteLine("資料庫已連線"); + } } } -} +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs index dcc2f14d..fb820c36 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs @@ -21,11 +21,7 @@ public string EmployeeDbConnectionString } } - private string _employeeDbConnectionString; private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; - public void Initial() - { - var memberDbConnectionString = this.EmployeeDbConnectionString; - } + private string _employeeDbConnectionString; } \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs index 8de5bc83..4e6b1019 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs @@ -30,7 +30,14 @@ public EmployeeDbContext(DbContextOptions options) { Console.WriteLine( $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); - this.Database.Migrate(); + if (this.Database.CanConnect() == false) + { + this.Database.EnsureCreated(); + } + else + { + this.Database.Migrate(); + } } s_migrated[0] = true; @@ -45,21 +52,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { p.HasKey(e => e.Id) .IsClustered(false); - + p.HasIndex(e => e.SequenceId) .IsUnique() .IsClustered(); - + p.Property(p => p.Remark) .IsRequired(false) - ; + ; }); modelBuilder.Entity(p => { p.HasKey(e => e.Employee_Id) .IsClustered(false); - + p.HasIndex(e => e.SequenceId) .IsUnique() .IsClustered(); diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj index d2a6aa4b..60ea4efc 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj @@ -12,6 +12,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln index 2cae23eb..c174fe2d 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln @@ -7,6 +7,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab.NETMiniProfiler.ASPNetC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.NETMiniProfiler.Infrastructure.EFCore5", "Lab.NETMiniProfiler.Infrastructure.EFCore5\Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj", "{083D436C-B451-4BCE-8A97-E6E77B9F9A23}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8E1AD56E-E673-4533-B933-3712BD42BD4D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{BF639261-D8D3-4F57-8682-C0262A5AFE04}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,4 +35,8 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1A39E53A-25EB-4546-9E76-DA1904FE5DCA} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} + {083D436C-B451-4BCE-8A97-E6E77B9F9A23} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} + EndGlobalSection EndGlobal diff --git a/Benchmark/Lab.NETMiniProfiler/docker-compose.yml b/Benchmark/Lab.NETMiniProfiler/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file From 41fa6a043c48af667dc172100e3a59665ef898a5 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 13:03:19 +0800 Subject: [PATCH 135/267] =?UTF-8?q?feat:=20add=20=E5=A4=A7=E8=B1=A1?= =?UTF-8?q?=E8=B3=87=E6=96=99=E5=BA=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/WeatherForecastController.cs | 34 ----------- .../Properties/launchSettings.json | 5 +- .../WeatherForecast.cs | 13 ---- .../AppDependencyInjectionExtensions.cs | 59 ++++++++++--------- .../EntityModel/EmployeeDbContext.cs | 17 +++--- ...MiniProfiler.Infrastructure.EFCore5.csproj | 1 + .../Lab.NETMiniProfiler/docker-compose.yml | 11 +++- 7 files changed, 53 insertions(+), 87 deletions(-) delete mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs deleted file mode 100644 index f1a485f1..00000000 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Lab.NETMiniProfiler.ASPNetCore5.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - this._logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json index 05ed2abd..e71f1320 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json @@ -6,7 +6,7 @@ "iisExpress": { "applicationUrl": "http://localhost:41185", "sslPort": 44361 - }, + } }, "profiles": { "Lab.NETMiniProfiler.ASPNetCore5": { @@ -17,7 +17,8 @@ "applicationUrl": "https://localhost:7186;http://localhost:5186", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" + "EMPLOYEE_DB_CONNECTION_STR": "Host=localhost;Port=5432;Database=member_service;Username=postgres;Password=guest", + "//EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" } }, "IIS Express": { diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs deleted file mode 100644 index de49d97c..00000000 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/WeatherForecast.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Lab.NETMiniProfiler.ASPNetCore5 -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs index 22a801f0..40a1fb3f 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs @@ -1,7 +1,9 @@ using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal; namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; @@ -9,44 +11,45 @@ public static class AppDependencyInjectionExtensions { public static void AddAppEnvironment(this IServiceCollection services) { - services.AddLogging(builder => - { - builder.AddConsole(); - }); + services.AddLogging(builder => { builder.AddConsole(); }); services.AddSingleton(); } public static void AddEntityFramework(this IServiceCollection services) { + // services.AddPooledDbContextFactory((provider, optionsBuilder) => + // { + // var option = provider.GetService(); + // var connectionString = option.EmployeeDbConnectionString; + // var loggerFactory = provider.GetService(); + // optionsBuilder.UseSqlServer(connectionString) + // .UseLoggerFactory(loggerFactory) + // ; + // }); + services.AddPooledDbContextFactory((provider, optionsBuilder) => { - var option = provider.GetService(); - var connectionString = option.EmployeeDbConnectionString; + // var mssqlOptions = optionsBuilder.Options.FindExtension(); + // var npgsqlOptions = optionsBuilder.Options.FindExtension(); + + var appOption = provider.GetService(); var loggerFactory = provider.GetService(); - optionsBuilder.UseSqlServer(connectionString) + var connectionString = appOption.EmployeeDbConnectionString; + // optionsBuilder.UseSqlServer(connectionString) + // .UseLoggerFactory(loggerFactory); + + optionsBuilder.UseNpgsql( + connectionString, //只會呼叫一次 + builder => + builder.EnableRetryOnFailure( + 10, + TimeSpan.FromSeconds(30), + new List { "57P01" })) + + // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() .UseLoggerFactory(loggerFactory) ; }); - - ; - - // services.AddPooledDbContextFactory((provider, options) => - // { - // var option = provider.GetService(); - // var loggerFactory = provider.GetService(); - // options.UseNpgsql( - // option.MemberDbConnectionString, //只會呼叫一次 - // builder => - // builder.EnableRetryOnFailure( - // 10, - // TimeSpan.FromSeconds(30), - // new List { "57P01" })) - // - // // .UseLazyLoadingProxies() - // .UseSnakeCaseNamingConvention() - // .UseLoggerFactory(loggerFactory) - // ; - // }); } - } \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs index 4e6b1019..a98e18d5 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs @@ -30,14 +30,15 @@ public EmployeeDbContext(DbContextOptions options) { Console.WriteLine( $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); - if (this.Database.CanConnect() == false) - { - this.Database.EnsureCreated(); - } - else - { - this.Database.Migrate(); - } + } + + if (this.Database.CanConnect() == false) + { + this.Database.EnsureCreated(); + } + else + { + this.Database.Migrate(); } s_migrated[0] = true; diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj index 60ea4efc..8f5315eb 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj @@ -13,5 +13,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Benchmark/Lab.NETMiniProfiler/docker-compose.yml b/Benchmark/Lab.NETMiniProfiler/docker-compose.yml index 8ecb1567..80c43a60 100644 --- a/Benchmark/Lab.NETMiniProfiler/docker-compose.yml +++ b/Benchmark/Lab.NETMiniProfiler/docker-compose.yml @@ -1,10 +1,17 @@ version: "3.8" services: - db-sql: + db-mssql: image: mcr.microsoft.com/mssql/server:2019-latest environment: - ACCEPT_EULA=Y - SA_PASSWORD=pass@w0rd1~ ports: - - 1433:1433 \ No newline at end of file + - 1433:1433 + + db-postgres: + image: postgres:12-alpine + environment: + - POSTGRES_PASSWORD=guest + ports: + - 5432:5432 \ No newline at end of file From 7084f4b44d12fa0f4589f4da28bcca656ddb28a0 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 22:05:45 +0800 Subject: [PATCH 136/267] add db type --- .../Properties/launchSettings.json | 4 +- .../AppDependencyInjectionExtensions.cs | 38 ++++++++++++------- .../AppEnvironmentOption.cs | 26 +++++++++++++ 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json index e71f1320..2c7f3c3c 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json @@ -18,7 +18,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "EMPLOYEE_DB_CONNECTION_STR": "Host=localhost;Port=5432;Database=member_service;Username=postgres;Password=guest", - "//EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True" + "DB_TYPE": "postgresSQL", + "//EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True", + "//DB_RTPE": "MsSQL" } }, "IIS Express": { diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs index 40a1fb3f..e42e5a84 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs @@ -1,4 +1,5 @@ using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel; +using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; @@ -6,7 +7,6 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal; namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; - public static class AppDependencyInjectionExtensions { public static void AddAppEnvironment(this IServiceCollection services) @@ -35,21 +35,31 @@ public static void AddEntityFramework(this IServiceCollection services) var appOption = provider.GetService(); var loggerFactory = provider.GetService(); var connectionString = appOption.EmployeeDbConnectionString; - // optionsBuilder.UseSqlServer(connectionString) - // .UseLoggerFactory(loggerFactory); + - optionsBuilder.UseNpgsql( - connectionString, //只會呼叫一次 - builder => - builder.EnableRetryOnFailure( - 10, - TimeSpan.FromSeconds(30), - new List { "57P01" })) + switch (appOption.DatabaseType) + { + case DatabaseType.MsSql: + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory); + break; + case DatabaseType.PostgresSQL: + optionsBuilder.UseNpgsql( + connectionString, //只會呼叫一次 + builder => + builder.EnableRetryOnFailure( + 10, + TimeSpan.FromSeconds(30), + new List { "57P01" })) - // .UseLazyLoadingProxies() - // .UseSnakeCaseNamingConvention() - .UseLoggerFactory(loggerFactory) - ; + // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + .UseLoggerFactory(loggerFactory) + ; + break; + default: + throw new ArgumentOutOfRangeException(); + } }); } } \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs index fb820c36..4f5eca78 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs @@ -1,7 +1,31 @@ namespace Lab.NETMiniProfiler.Infrastructure.EFCore5; +public enum DatabaseType +{ + MsSql = 1, + PostgresSQL = 2 +} + public class AppEnvironmentOption { + public DatabaseType DatabaseType + { + get + { + if (this._databaseType.HasValue == false) + { + var variable = EnvironmentAssistant.GetEnvironmentVariable(this.DATABASE_TYPE); + if (Enum.TryParse(variable,true, out DatabaseType result)) + { + this._databaseType = result; + } + } + + return this._databaseType.Value; + } + set => this._databaseType = value; + } + public string EmployeeDbConnectionString { get @@ -21,7 +45,9 @@ public string EmployeeDbConnectionString } } + private readonly string DATABASE_TYPE = "DB_TYPE"; private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + private DatabaseType? _databaseType; private string _employeeDbConnectionString; } \ No newline at end of file From 657348f97e9e1bf911a7969ea06372950793e760 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 23:13:53 +0800 Subject: [PATCH 137/267] feat: add asp.net core 6 --- .../Controllers/ValuesController.cs | 4 +- .../Controllers/ValuesController.cs | 79 +++++++++++++++++++ .../Lab.NETMiniProfiler.ASPNetCore6.csproj | 20 +++++ .../Program.cs | 32 ++++++++ .../Properties/launchSettings.json | 36 +++++++++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 +++ .../AppDependencyInjectionExtensions.cs | 61 +++++++------- .../AppEnvironmentOption.cs | 34 ++++++-- .../EntityModel/Employee.cs | 2 +- .../EntityModel/EmployeeDbContext.cs | 32 ++++---- .../EntityModel/Identity.cs | 2 +- .../EntityModel/OrderHistory.cs | 2 +- .../EnvironmentAssistant.cs | 2 +- ...MiniProfiler.Infrastructure.EFCore6.csproj | 9 ++- .../Lab.NETMiniProfiler.sln | 14 ++++ 16 files changed, 284 insertions(+), 62 deletions(-) create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Controllers/ValuesController.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Properties/launchSettings.json create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.Development.json create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.json diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs index 811a23c0..a926f283 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs @@ -50,7 +50,7 @@ public ActionResult Get(int id) // POST api/values [HttpPost] - public async Task Post(CancellationToken cancellationToken = default) + public async Task Post(CancellationToken cancel = default) { using (MiniProfiler.Current.Step("異動資料庫")) { @@ -65,7 +65,7 @@ public async Task Post(CancellationToken cancellationToken = defa Name = Faker.Name.Suffix(), }; db.Employees.Add(toDb); - await db.SaveChangesAsync(cancellationToken); + await db.SaveChangesAsync(cancel); return this.Ok(toDb); } } diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Controllers/ValuesController.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Controllers/ValuesController.cs new file mode 100644 index 00000000..b9dd0977 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Controllers/ValuesController.cs @@ -0,0 +1,79 @@ +using Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using StackExchange.Profiling; + +namespace Lab.NETMiniProfiler.ASPNetCore6.Controllers +{ + /// + /// Value Controller + /// + [Route("[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + private readonly IDbContextFactory _employeeDbContextFactory; + + public ValuesController(IDbContextFactory employeeDbContextFactory) + { + this._employeeDbContextFactory = employeeDbContextFactory; + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + + /// + /// Get Api + /// + /// + + // GET api/values + [HttpGet] + public async Task Get(CancellationToken cancel = default) + { + using (MiniProfiler.Current.Step("查詢資料庫")) + { + await using var db = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + return this.Ok(await db.Employees.AsTracking().ToListAsync(cancel)); + } + } + + // GET api/values/5 + [HttpGet("{id}")] + public ActionResult Get(int id) + { + return "value"; + } + + // POST api/values + [HttpPost] + public async Task Post(CancellationToken cancel = default) + { + using (MiniProfiler.Current.Step("異動資料庫")) + { + await using var db = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + + var toDb = new Employee + { + Id = Guid.NewGuid(), + CreateAt = DateTimeOffset.Now, + CreateBy = Faker.Name.FullName(), + Age = Faker.RandomNumber.Next(1, 100), + Name = Faker.Name.Suffix(), + }; + db.Employees.Add(toDb); + await db.SaveChangesAsync(cancel); + return this.Ok(toDb); + } + } + + // PUT api/values/5 + [HttpPut("{id}")] + public void Put(int id, [FromBody] string value) + { + } + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj new file mode 100644 index 00000000..9ea80629 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs new file mode 100644 index 00000000..84c81f06 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs @@ -0,0 +1,32 @@ +using Lab.NETMiniProfiler.Infrastructure.EFCore6; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddMiniProfiler(o => o.RouteBasePath = "/profiler") + .AddEntityFramework(); +builder.Services.AddAppEnvironment(); +builder.Services.AddEntityFramework(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseMiniProfiler(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Properties/launchSettings.json new file mode 100644 index 00000000..b6348197 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Properties/launchSettings.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:30924", + "sslPort": 44345 + } + }, + "profiles": { + "Lab.NETMiniProfiler.ASPNetCore6": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7139;http://localhost:5139", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "EMPLOYEE_DB_CONNECTION_STR": "Host=localhost;Port=5432;Database=member_service;Username=postgres;Password=guest", + "DB_TYPE": "postgresSQL", + "//EMPLOYEE_DB_CONNECTION_STR": "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True", + "//DB_RTPE": "MsSQL" + + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.Development.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.json b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs index 094946b8..7230b81e 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs @@ -1,18 +1,14 @@ -using Lab.NETMiniProfiler.Infrastructure.EntityModel; +using Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Lab.NETMiniProfiler.Infrastructure; - +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6; public static class AppDependencyInjectionExtensions { public static void AddAppEnvironment(this IServiceCollection services) { - services.AddLogging(builder => - { - builder.AddConsole(); - }); + services.AddLogging(builder => { builder.AddConsole(); }); services.AddSingleton(); } @@ -20,33 +16,34 @@ public static void AddEntityFramework(this IServiceCollection services) { services.AddPooledDbContextFactory((provider, optionsBuilder) => { - var option = provider.GetService(); - var connectionString = option.EmployeeDbConnectionString; + var appOption = provider.GetService(); var loggerFactory = provider.GetService(); - optionsBuilder.UseSqlServer(connectionString) - .UseLoggerFactory(loggerFactory) - ; - }); + var connectionString = appOption.EmployeeDbConnectionString; + - ; + switch (appOption.DatabaseType) + { + case DatabaseType.MsSql: + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory); + break; + case DatabaseType.PostgresSQL: + optionsBuilder.UseNpgsql( + connectionString, //只會呼叫一次 + builder => + builder.EnableRetryOnFailure( + 10, + TimeSpan.FromSeconds(30), + new List { "57P01" })) - // services.AddPooledDbContextFactory((provider, options) => - // { - // var option = provider.GetService(); - // var loggerFactory = provider.GetService(); - // options.UseNpgsql( - // option.MemberDbConnectionString, //只會呼叫一次 - // builder => - // builder.EnableRetryOnFailure( - // 10, - // TimeSpan.FromSeconds(30), - // new List { "57P01" })) - // - // // .UseLazyLoadingProxies() - // .UseSnakeCaseNamingConvention() - // .UseLoggerFactory(loggerFactory) - // ; - // }); + // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + .UseLoggerFactory(loggerFactory) + ; + break; + default: + throw new ArgumentOutOfRangeException(); + } + }); } - } \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs index 42da1ef1..6614f3be 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs @@ -1,7 +1,31 @@ -namespace Lab.NETMiniProfiler.Infrastructure; +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6; + +public enum DatabaseType +{ + MsSql = 1, + PostgresSQL = 2 +} public class AppEnvironmentOption { + public DatabaseType DatabaseType + { + get + { + if (this._databaseType.HasValue == false) + { + var variable = EnvironmentAssistant.GetEnvironmentVariable(this.DATABASE_TYPE); + if (Enum.TryParse(variable,true, out DatabaseType result)) + { + this._databaseType = result; + } + } + + return this._databaseType.Value; + } + set => this._databaseType = value; + } + public string EmployeeDbConnectionString { get @@ -21,11 +45,9 @@ public string EmployeeDbConnectionString } } - private string _employeeDbConnectionString; + private readonly string DATABASE_TYPE = "DB_TYPE"; private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + private DatabaseType? _databaseType; - public void Initial() - { - var memberDbConnectionString = this.EmployeeDbConnectionString; - } + private string _employeeDbConnectionString; } \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs index cf11b247..1799f47d 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Employee.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel { [Table("Employee")] public class Employee diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs index f07f9b6b..14beaacf 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs @@ -1,8 +1,7 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; -namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel { public class EmployeeDbContext : DbContext { @@ -26,17 +25,20 @@ public EmployeeDbContext(DbContextOptions options) { if (s_migrated[0] == false) { - var memoryOptions = options.FindExtension(); + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + } - if (memoryOptions == null) + if (this.Database.CanConnect() == false) + { + this.Database.EnsureCreated(); + } + else { - var sqlOptions = options.FindExtension(); - if (sqlOptions != null) - { - Console.WriteLine( - $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); - this.Database.Migrate(); - } + this.Database.Migrate(); } s_migrated[0] = true; @@ -51,21 +53,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { p.HasKey(e => e.Id) .IsClustered(false); - + p.HasIndex(e => e.SequenceId) .IsUnique() .IsClustered(); - + p.Property(p => p.Remark) .IsRequired(false) - ; + ; }); modelBuilder.Entity(p => { p.HasKey(e => e.Employee_Id) .IsClustered(false); - + p.HasIndex(e => e.SequenceId) .IsUnique() .IsClustered(); diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs index 57b2a437..47f791e7 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/Identity.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel { [Table("Identity")] public class Identity diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs index 63c19ce4..f582242b 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/OrderHistory.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.NETMiniProfiler.Infrastructure.EntityModel +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel { [Table("OrderHistory")] public class OrderHistory diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs index 7f2ff85c..bc7c8f51 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs @@ -1,4 +1,4 @@ -namespace Lab.NETMiniProfiler.Infrastructure; +namespace Lab.NETMiniProfiler.Infrastructure.EFCore6; public class EnvironmentAssistant { diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj index c5a492b3..2af7dc9f 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj @@ -4,12 +4,15 @@ net6.0 enable enable - Lab.NETMiniProfiler.Infrastructure.EFCore6 + 10 - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln index c174fe2d..5f6fca96 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln @@ -14,6 +14,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{BF6392 docker-compose.yml = docker-compose.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.NETMiniProfiler.ASPNetCore6", "Lab.NETMiniProfiler.ASPNetCore6\Lab.NETMiniProfiler.ASPNetCore6.csproj", "{A677D49D-8088-44DD-9900-FC7694C30D70}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.NETMiniProfiler.Infrastructure.EFCore6", "Lab.NETMiniProfiler.Infrastructure.EFCore6\Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj", "{D4CB746B-5711-4338-B716-763A86822134}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,6 +32,14 @@ Global {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Debug|Any CPU.Build.0 = Debug|Any CPU {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Release|Any CPU.ActiveCfg = Release|Any CPU {083D436C-B451-4BCE-8A97-E6E77B9F9A23}.Release|Any CPU.Build.0 = Release|Any CPU + {A677D49D-8088-44DD-9900-FC7694C30D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A677D49D-8088-44DD-9900-FC7694C30D70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A677D49D-8088-44DD-9900-FC7694C30D70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A677D49D-8088-44DD-9900-FC7694C30D70}.Release|Any CPU.Build.0 = Release|Any CPU + {D4CB746B-5711-4338-B716-763A86822134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4CB746B-5711-4338-B716-763A86822134}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4CB746B-5711-4338-B716-763A86822134}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4CB746B-5711-4338-B716-763A86822134}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -38,5 +50,7 @@ Global GlobalSection(NestedProjects) = preSolution {8D4D97D5-C7F1-4BD7-9FE8-7A3766DB8D04} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} {083D436C-B451-4BCE-8A97-E6E77B9F9A23} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} + {A677D49D-8088-44DD-9900-FC7694C30D70} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} + {D4CB746B-5711-4338-B716-763A86822134} = {8E1AD56E-E673-4533-B933-3712BD42BD4D} EndGlobalSection EndGlobal From 47bb47ed8fe87fb9585a093dc67a9ef48ea5f7e3 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 16 Jan 2022 23:21:12 +0800 Subject: [PATCH 138/267] =?UTF-8?q?MiniProfiler=20=E6=95=B4=E5=90=88=20Swa?= =?UTF-8?q?gger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Startup.cs | 4 +- .../Lab.NETMiniProfiler.ASPNetCore6.csproj | 4 + .../Program.cs | 27 ++++- .../index.html | 99 +++++++++++++++++++ 4 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/index.html diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs index fd257d87..8d925b6b 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs @@ -39,7 +39,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseMiniProfiler(); } - VerifyDbConnection(app); + PreConnectionDb(app); app.UseHttpsRedirection(); @@ -65,7 +65,7 @@ public void ConfigureServices(IServiceCollection services) services.AddEntityFramework(); } - private static void VerifyDbConnection(IApplicationBuilder app) + private static void PreConnectionDb(IApplicationBuilder app) { var employeeDbContextFactory = app.ApplicationServices.GetService>(); diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj index 9ea80629..de1b6b14 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs index 84c81f06..add4dbc4 100644 --- a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs @@ -1,4 +1,8 @@ +using System.Diagnostics; +using System.Reflection; using Lab.NETMiniProfiler.Infrastructure.EFCore6; +using Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel; +using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); @@ -14,12 +18,21 @@ builder.Services.AddAppEnvironment(); builder.Services.AddEntityFramework(); var app = builder.Build(); +PreConnectionDb(app); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + // app.UseSwaggerUI(); + app.UseSwaggerUI(c => + { + c.RoutePrefix = "swagger"; + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + c.IndexStream = () => typeof(Program).GetTypeInfo() + .Assembly + .GetManifestResourceStream("Lab.NETMiniProfiler.ASPNetCore6.index.html"); + }); app.UseMiniProfiler(); } @@ -28,5 +41,15 @@ app.UseAuthorization(); app.MapControllers(); - app.Run(); + +static void PreConnectionDb(IApplicationBuilder app) +{ + var employeeDbContextFactory = + app.ApplicationServices.GetService>(); + var db = employeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + Debug.WriteLine("資料庫已連線"); + } +} \ No newline at end of file diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/index.html b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/index.html new file mode 100644 index 00000000..49c7aa3d --- /dev/null +++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/index.html @@ -0,0 +1,99 @@ + + + + + + + + %(DocumentTitle) + + + + + + + %(HeadContent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + From 036dfd3dddd392635caf3a9493e0e083b4673d5e Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 17 Jan 2022 23:12:48 +0800 Subject: [PATCH 139/267] remove file --- .../Controllers/ValueController.cs | 54 ---------- .../Controllers/WeatherForecastController.cs | 39 -------- .../WebApplication1/Program.cs | 26 ----- .../Properties/launchSettings.json | 31 ------ .../WebApplication1/Startup.cs | 74 -------------- .../WebApplication1/WeatherForecast.cs | 15 --- .../WebApplication1/WebApplication1.csproj | 21 ---- .../appsettings.Development.json | 9 -- .../WebApplication1/appsettings.json | 10 -- .../WebApplication1/index.html | 99 ------------------- 10 files changed, 378 deletions(-) delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json delete mode 100644 Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs deleted file mode 100644 index 7ff8d74c..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/ValueController.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using StackExchange.Profiling; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace WebApplication1.Controllers -{ - [ApiController] - [Route("[controller]")] - public class ValueController : ControllerBase - { - [HttpGet] - public IEnumerable Get() - { - string url1 = string.Empty; - string url2 = string.Empty; - using (MiniProfiler.Current.Step("Get方法")) - { - using (MiniProfiler.Current.Step("准备数据")) - { - using (MiniProfiler.Current.CustomTiming("SQL", "SELECT * FROM Config")) - { - // 模拟一个SQL查询 - Thread.Sleep(500); - - url1 = "https://www.baidu.com"; - url2 = "https://www.sina.com.cn/"; - } - } - - - using (MiniProfiler.Current.Step("使用从数据库中查询的数据,进行Http请求")) - { - using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url1)) - { - var client = new WebClient(); - var reply = client.DownloadString(url1); - } - - using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url2)) - { - var client = new WebClient(); - var reply = client.DownloadString(url2); - } - } - } - return new string[] { "value1", "value2" }; - } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs deleted file mode 100644 index 47134b11..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace WebApplication1.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs deleted file mode 100644 index 53f3427e..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace WebApplication1 -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json deleted file mode 100644 index 1496716e..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Properties/launchSettings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:64791", - "sslPort": 44397 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "WebApplication1": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs deleted file mode 100644 index 10a9665d..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/Startup.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; - -namespace WebApplication1 -{ - 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.AddControllers(); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); - }); - - services.AddMiniProfiler(options => - options.RouteBasePath = "/profiler" - ); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseMiniProfiler(); - - app.UseSwagger(); - //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); - app.UseSwaggerUI(c => - { - c.RoutePrefix = "swagger"; - c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - c.IndexStream = () => GetType() - .GetTypeInfo() - .Assembly - .GetManifestResourceStream("WebApplication1.index.html"); - }); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs deleted file mode 100644 index 11a0296b..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace WebApplication1 -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj b/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj deleted file mode 100644 index cd66119c..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/WebApplication1.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net5.0 - - - - - - - - - - - - - - - - - diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json deleted file mode 100644 index 8983e0fc..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json b/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json deleted file mode 100644 index d9d9a9bf..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html b/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html deleted file mode 100644 index 49c7aa3d..00000000 --- a/Benchmark/Lab.NETMiniProfiler/WebApplication1/index.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - %(DocumentTitle) - - - - - - - %(HeadContent) - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - From 720e9f2c5c879469f7a298d97faa82247053a6a4 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 02:26:59 +0800 Subject: [PATCH 140/267] feat: add new project --- .../Lab.MemoryConfigSource.TestProject.csproj | 19 +++++++ .../UnitTest1.cs | 51 +++++++++++++++++++ .../Lab.MemoryConfigSource.sln | 16 ++++++ 3 files changed, 86 insertions(+) create mode 100644 Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj create mode 100644 Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs create mode 100644 Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj new file mode 100644 index 00000000..c752da40 --- /dev/null +++ b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs new file mode 100644 index 00000000..83eec006 --- /dev/null +++ b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MemoryConfigSource.TestProject; + +public enum Gender +{ + Male, + Female +} + +public class Member +{ + public string Id { get; set; } + + public Profile Profile { get; set; } +} + +public class Profile +{ + public Gender Gender { get; set; } + + public int Age { get; set; } + + public string Address { get; set; } +} + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void 綁定複雜型別() + { + var source = new Dictionary + { + ["id"] = "9527", + ["profile:gender"] = "Male", + ["profile:age"] = "18", + ["profile:address"] = "Taipei", + }; + var builder = new ConfigurationBuilder(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get(); + + Assert.AreEqual("9527", member.Id); + Assert.AreEqual(18, member.Profile.Age); + Assert.AreEqual("Taipei", member.Profile.Address); + Assert.AreEqual(Gender.Male, member.Profile.Gender); + } +} \ No newline at end of file diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln new file mode 100644 index 00000000..b6a84778 --- /dev/null +++ b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.MemoryConfigSource.TestProject", "Lab.MemoryConfigSource.TestProject\Lab.MemoryConfigSource.TestProject.csproj", "{531A25A6-0836-4950-85B3-3882B19FD18F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {531A25A6-0836-4950-85B3-3882B19FD18F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {531A25A6-0836-4950-85B3-3882B19FD18F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {531A25A6-0836-4950-85B3-3882B19FD18F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {531A25A6-0836-4950-85B3-3882B19FD18F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From a497b26f319ce2313c74f6809c72bc1c1460ca7e Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 08:46:39 +0800 Subject: [PATCH 141/267] rename --- .../Lab.ConfigBind.TestProject.csproj} | 6 +- .../Lab.ConfigBind.TestProject/UnitTest1.cs | 102 ++++++++++++++++++ .../Lab.ConfigBind/Lab.ConfigBind.sln | 16 +++ .../UnitTest1.cs | 51 --------- .../Lab.MemoryConfigSource.sln | 16 --- 5 files changed, 122 insertions(+), 69 deletions(-) rename Configuration/{Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj => Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj} (86%) create mode 100644 Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs create mode 100644 Configuration/Lab.ConfigBind/Lab.ConfigBind.sln delete mode 100644 Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs delete mode 100644 Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj similarity index 86% rename from Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj rename to Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj index c752da40..67c4441e 100644 --- a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/Lab.MemoryConfigSource.TestProject.csproj +++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj @@ -8,12 +8,14 @@ - - + + + + diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs new file mode 100644 index 00000000..4507739c --- /dev/null +++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ConfigBind.TestProject; + + + +[TestClass] +public class UnitTest1 +{ + public enum Gender + { + Male, + Female + } + + public class Member + { + public string Id { get; set; } + + public Profile Profile { get; set; } + } + + public class Profile + { + public Gender? Gender { get; set; } + + public int? Age { get; set; } + + public string Address { get; set; } + } + + [TestMethod] + public void 綁定複雜型別() + { + var source = new Dictionary + { + ["id"] = "9527", + ["profile:gender"] = "Male", + ["profile:age"] = "18", + ["profile:address"] = "Taipei", + }; + + var builder = new ConfigurationBuilder(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get(); + + Assert.AreEqual("9527", member.Id); + Assert.AreEqual(18, member.Profile.Age); + Assert.AreEqual("Taipei", member.Profile.Address); + Assert.AreEqual(Gender.Male, member.Profile.Gender); + } + [TestMethod] + public void 綁定集合() + { + var source = new Dictionary + { + ["a:id"] = "9527", + ["a:profile:gender"] = "Male", + ["a:profile:age"] = "18", + ["a:profile:address"] = "Taipei", + ["b:id"] = "9528", + ["b:profile:gender"] = "Male", + ["b:profile:age"] = "19", + ["b:profile:address"] = "Taipei", + + }; + + var builder = new ConfigurationBuilder(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get>(); + + Assert.AreEqual("9527", member[0].Id); + Assert.AreEqual("9528", member[1].Id); + } + + [TestMethod] + public void 綁定環境變數() + { + var source = new Dictionary + { + ["a:id"] = "9527", + ["a:profile:gender"] = "Male", + ["a:profile:age"] = "18", + ["a:profile:address"] = "Taipei", + ["b:id"] = "9528", + ["b:profile:gender"] = "Male", + ["b:profile:age"] = "19", + ["b:profile:address"] = "Taipei", + + }; + var builder = new ConfigurationBuilder(); + builder.AddEnvironmentVariables(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get>(); + + Assert.AreEqual("9527", member[0].Id); + Assert.AreEqual("9528", member[1].Id); + } + +} \ No newline at end of file diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.sln b/Configuration/Lab.ConfigBind/Lab.ConfigBind.sln new file mode 100644 index 00000000..10724763 --- /dev/null +++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ConfigBind.TestProject", "Lab.ConfigBind.TestProject\Lab.ConfigBind.TestProject.csproj", "{3733D824-84BE-4993-A321-7DECC340FA64}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3733D824-84BE-4993-A321-7DECC340FA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3733D824-84BE-4993-A321-7DECC340FA64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3733D824-84BE-4993-A321-7DECC340FA64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3733D824-84BE-4993-A321-7DECC340FA64}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs deleted file mode 100644 index 83eec006..00000000 --- a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.TestProject/UnitTest1.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Lab.MemoryConfigSource.TestProject; - -public enum Gender -{ - Male, - Female -} - -public class Member -{ - public string Id { get; set; } - - public Profile Profile { get; set; } -} - -public class Profile -{ - public Gender Gender { get; set; } - - public int Age { get; set; } - - public string Address { get; set; } -} - -[TestClass] -public class UnitTest1 -{ - [TestMethod] - public void 綁定複雜型別() - { - var source = new Dictionary - { - ["id"] = "9527", - ["profile:gender"] = "Male", - ["profile:age"] = "18", - ["profile:address"] = "Taipei", - }; - var builder = new ConfigurationBuilder(); - var configRoot = builder.AddInMemoryCollection(source).Build(); - var member = configRoot.Get(); - - Assert.AreEqual("9527", member.Id); - Assert.AreEqual(18, member.Profile.Age); - Assert.AreEqual("Taipei", member.Profile.Address); - Assert.AreEqual(Gender.Male, member.Profile.Gender); - } -} \ No newline at end of file diff --git a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln b/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln deleted file mode 100644 index b6a84778..00000000 --- a/Configuration/Lab.MemoryConfigSource/Lab.MemoryConfigSource.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.MemoryConfigSource.TestProject", "Lab.MemoryConfigSource.TestProject\Lab.MemoryConfigSource.TestProject.csproj", "{531A25A6-0836-4950-85B3-3882B19FD18F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {531A25A6-0836-4950-85B3-3882B19FD18F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {531A25A6-0836-4950-85B3-3882B19FD18F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {531A25A6-0836-4950-85B3-3882B19FD18F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {531A25A6-0836-4950-85B3-3882B19FD18F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal From f0d8f6dd3484cbd725e10328182fc469cfe4cf47 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 08:48:21 +0800 Subject: [PATCH 142/267] add taskfile to sln --- Configuration/Lab.Environment/Lab.Environment.sln | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Configuration/Lab.Environment/Lab.Environment.sln b/Configuration/Lab.Environment/Lab.Environment.sln index 9623d29e..291bd670 100644 --- a/Configuration/Lab.Environment/Lab.Environment.sln +++ b/Configuration/Lab.Environment/Lab.Environment.sln @@ -4,6 +4,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Environment.ConsoleApp. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Environment.ConsoleApp.NET6", "Lab.Environment.ConsoleApp.NET6\Lab.Environment.ConsoleApp.NET6.csproj", "{5033EB4D-63B9-4F17-9FDD-1A81E174F3C9}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{6ACD7D50-37E9-4003-B8BE-17FC3724B567}" + ProjectSection(SolutionItems) = preProject + Taskfile.yml = Taskfile.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From df4980f20d1ceb8d9841bdc9097400de288b332e Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 10:14:10 +0800 Subject: [PATCH 143/267] =?UTF-8?q?feat:=E5=AD=97=E5=85=B8=E9=9B=86?= =?UTF-8?q?=E5=90=88=E7=B6=81=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.ConfigBind.TestProject.csproj | 14 +-- ...27\345\205\270\351\233\206\345\220\210.cs" | 92 +++++++++---------- ...60\345\242\203\350\256\212\346\225\270.cs" | 72 +++++++++++++++ 3 files changed, 123 insertions(+), 55 deletions(-) rename Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs => "Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" (67%) create mode 100644 "Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj index 67c4441e..dc5bec91 100644 --- a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj +++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj @@ -8,13 +8,13 @@ - - - - - - - + + + + + + + diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" similarity index 67% rename from Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs rename to "Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" index 4507739c..bbf78098 100644 --- a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/UnitTest1.cs +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" @@ -4,31 +4,30 @@ namespace Lab.ConfigBind.TestProject; - - [TestClass] -public class UnitTest1 +public class 來源為字典集合 { - public enum Gender - { - Male, - Female - } - - public class Member - { - public string Id { get; set; } - - public Profile Profile { get; set; } - } - - public class Profile + [TestMethod] + public void 綁定集合() { - public Gender? Gender { get; set; } + var source = new Dictionary + { + ["a:id"] = "9527", + ["a:profile:gender"] = "Male", + ["a:profile:age"] = "18", + ["a:profile:address"] = "Taipei", + ["b:id"] = "9528", + ["b:profile:gender"] = "Male", + ["b:profile:age"] = "19", + ["b:profile:address"] = "Taipei", + }; - public int? Age { get; set; } + var builder = new ConfigurationBuilder(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get>(); - public string Address { get; set; } + Assert.AreEqual("9527", member[0].Id); + Assert.AreEqual("9528", member[1].Id); } [TestMethod] @@ -41,39 +40,16 @@ public void 綁定複雜型別() ["profile:age"] = "18", ["profile:address"] = "Taipei", }; - + var builder = new ConfigurationBuilder(); var configRoot = builder.AddInMemoryCollection(source).Build(); var member = configRoot.Get(); - + Assert.AreEqual("9527", member.Id); Assert.AreEqual(18, member.Profile.Age); Assert.AreEqual("Taipei", member.Profile.Address); Assert.AreEqual(Gender.Male, member.Profile.Gender); } - [TestMethod] - public void 綁定集合() - { - var source = new Dictionary - { - ["a:id"] = "9527", - ["a:profile:gender"] = "Male", - ["a:profile:age"] = "18", - ["a:profile:address"] = "Taipei", - ["b:id"] = "9528", - ["b:profile:gender"] = "Male", - ["b:profile:age"] = "19", - ["b:profile:address"] = "Taipei", - - }; - - var builder = new ConfigurationBuilder(); - var configRoot = builder.AddInMemoryCollection(source).Build(); - var member = configRoot.Get>(); - - Assert.AreEqual("9527", member[0].Id); - Assert.AreEqual("9528", member[1].Id); - } [TestMethod] public void 綁定環境變數() @@ -88,15 +64,35 @@ public void 綁定環境變數() ["b:profile:gender"] = "Male", ["b:profile:age"] = "19", ["b:profile:address"] = "Taipei", - }; var builder = new ConfigurationBuilder(); builder.AddEnvironmentVariables(); var configRoot = builder.AddInMemoryCollection(source).Build(); var member = configRoot.Get>(); - + Assert.AreEqual("9527", member[0].Id); Assert.AreEqual("9528", member[1].Id); } - + + private enum Gender + { + Male, + Female + } + + private class Member + { + public string Id { get; set; } + + public Profile Profile { get; set; } + } + + private class Profile + { + public Gender? Gender { get; set; } + + public int? Age { get; set; } + + public string Address { get; set; } + } } \ No newline at end of file diff --git "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" new file mode 100644 index 00000000..c79359a9 --- /dev/null +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ConfigBind.TestProject; + +[TestClass] +public class 來源為環境變數 +{ + [TestMethod] + public void 綁定複雜型別() + { + Environment.SetEnvironmentVariable("id", "9527"); + Environment.SetEnvironmentVariable("profile:gender", "Male"); + Environment.SetEnvironmentVariable("profile:age", "18"); + Environment.SetEnvironmentVariable("profile:address", "Taipei"); + + var builder = new ConfigurationBuilder(); + builder.AddEnvironmentVariables(); + var configRoot = builder.AddInMemoryCollection() + .Build(); + var member = configRoot.Get(); + Assert.AreEqual("9527", member.Id); + Assert.AreEqual(18, member.Profile.Age); + Assert.AreEqual("Taipei", member.Profile.Address); + Assert.AreEqual(Gender.Male, member.Profile.Gender); + } + + [TestMethod] + public void 綁定集合() + { + Environment.SetEnvironmentVariable("a:id", "9527"); + Environment.SetEnvironmentVariable("a:profile:gender", "Male"); + Environment.SetEnvironmentVariable("a:profile:age", "18"); + Environment.SetEnvironmentVariable("a:profile:address", "Taipei"); + Environment.SetEnvironmentVariable("b:id", "9528"); + Environment.SetEnvironmentVariable("b:profile:gender", "Male"); + Environment.SetEnvironmentVariable("b:profile:age", "19"); + Environment.SetEnvironmentVariable("b:profile:address", "Taipei"); + var builder = new ConfigurationBuilder(); + builder.AddEnvironmentVariables(); + var configRoot = builder.AddInMemoryCollection() + .Build(); + var member = configRoot.Get>(); + + Assert.AreEqual("9527", member[0].Id); + Assert.AreEqual("9528", member[1].Id); + } + + private enum Gender + { + Male, + Female + } + + private class Member + { + public string Id { get; set; } + + public Profile Profile { get; set; } + } + + private class Profile + { + public Gender? Gender { get; set; } + + public int? Age { get; set; } + + public string Address { get; set; } + } +} \ No newline at end of file From 8630ea161733d2840c3956d3767bea7707718ffa Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 15:13:16 +0800 Subject: [PATCH 144/267] refactor --- .../Lab.ConfigBind.TestProject.csproj | 14 +++--- ...27\345\205\270\351\233\206\345\220\210.cs" | 46 +++++++++---------- ...60\345\242\203\350\256\212\346\225\270.cs" | 43 +++++++++++------ 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj index dc5bec91..67c4441e 100644 --- a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj +++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj @@ -8,13 +8,13 @@ - - - - - - - + + + + + + + diff --git "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" index bbf78098..80b674d8 100644 --- "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" @@ -7,6 +7,29 @@ namespace Lab.ConfigBind.TestProject; [TestClass] public class 來源為字典集合 { + [TestMethod] + public void 綁定字典() + { + var source = new Dictionary + { + ["a:id"] = "9527", + ["a:profile:gender"] = "Male", + ["a:profile:age"] = "18", + ["a:profile:address"] = "Taipei", + ["b:id"] = "9528", + ["b:profile:gender"] = "Male", + ["b:profile:age"] = "19", + ["b:profile:address"] = "Taipei", + }; + + var builder = new ConfigurationBuilder(); + var configRoot = builder.AddInMemoryCollection(source).Build(); + var member = configRoot.Get>(); + + Assert.AreEqual("9527", member["a"].Id); + Assert.AreEqual("9528", member["b"].Id); + } + [TestMethod] public void 綁定集合() { @@ -51,29 +74,6 @@ public void 綁定複雜型別() Assert.AreEqual(Gender.Male, member.Profile.Gender); } - [TestMethod] - public void 綁定環境變數() - { - var source = new Dictionary - { - ["a:id"] = "9527", - ["a:profile:gender"] = "Male", - ["a:profile:age"] = "18", - ["a:profile:address"] = "Taipei", - ["b:id"] = "9528", - ["b:profile:gender"] = "Male", - ["b:profile:age"] = "19", - ["b:profile:address"] = "Taipei", - }; - var builder = new ConfigurationBuilder(); - builder.AddEnvironmentVariables(); - var configRoot = builder.AddInMemoryCollection(source).Build(); - var member = configRoot.Get>(); - - Assert.AreEqual("9527", member[0].Id); - Assert.AreEqual("9528", member[1].Id); - } - private enum Gender { Male, diff --git "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" index c79359a9..4020acb1 100644 --- "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" @@ -9,22 +9,20 @@ namespace Lab.ConfigBind.TestProject; public class 來源為環境變數 { [TestMethod] - public void 綁定複雜型別() + public void 綁定字典() { - Environment.SetEnvironmentVariable("id", "9527"); - Environment.SetEnvironmentVariable("profile:gender", "Male"); - Environment.SetEnvironmentVariable("profile:age", "18"); - Environment.SetEnvironmentVariable("profile:address", "Taipei"); - + Environment.SetEnvironmentVariable("a:id", "9527"); + Environment.SetEnvironmentVariable("a:profile:gender", "Male"); + Environment.SetEnvironmentVariable("a:profile:age", "18"); + Environment.SetEnvironmentVariable("a:profile:address", "Taipei"); + Environment.SetEnvironmentVariable("b:id", "9528"); + Environment.SetEnvironmentVariable("b:profile:gender", "Male"); + Environment.SetEnvironmentVariable("b:profile:age", "19"); + Environment.SetEnvironmentVariable("b:profile:address", "Taipei"); var builder = new ConfigurationBuilder(); - builder.AddEnvironmentVariables(); var configRoot = builder.AddInMemoryCollection() .Build(); - var member = configRoot.Get(); - Assert.AreEqual("9527", member.Id); - Assert.AreEqual(18, member.Profile.Age); - Assert.AreEqual("Taipei", member.Profile.Address); - Assert.AreEqual(Gender.Male, member.Profile.Gender); + var member = configRoot.Get>(); } [TestMethod] @@ -39,15 +37,34 @@ public void 綁定集合() Environment.SetEnvironmentVariable("b:profile:age", "19"); Environment.SetEnvironmentVariable("b:profile:address", "Taipei"); var builder = new ConfigurationBuilder(); - builder.AddEnvironmentVariables(); var configRoot = builder.AddInMemoryCollection() .Build(); var member = configRoot.Get>(); + var member2 = configRoot.Get>(); Assert.AreEqual("9527", member[0].Id); Assert.AreEqual("9528", member[1].Id); } + [TestMethod] + public void 綁定複雜型別() + { + Environment.SetEnvironmentVariable("id", "9527"); + Environment.SetEnvironmentVariable("profile:gender", "Male"); + Environment.SetEnvironmentVariable("profile:age", "18"); + Environment.SetEnvironmentVariable("profile:address", "Taipei"); + + var builder = new ConfigurationBuilder(); + builder.AddEnvironmentVariables(); + var configRoot = builder.AddInMemoryCollection() + .Build(); + var member = configRoot.Get(); + Assert.AreEqual("9527", member.Id); + Assert.AreEqual(18, member.Profile.Age); + Assert.AreEqual("Taipei", member.Profile.Address); + Assert.AreEqual(Gender.Male, member.Profile.Gender); + } + private enum Gender { Male, From 965ce4b8c01d161aab26b0a6a5effa763e8df65c Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 22:09:45 +0800 Subject: [PATCH 145/267] fix bug --- ...45\255\227\345\205\270\351\233\206\345\220\210.cs" | 2 +- ...47\222\260\345\242\203\350\256\212\346\225\270.cs" | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" index 80b674d8..0565a7fe 100644 --- "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\345\255\227\345\205\270\351\233\206\345\220\210.cs" @@ -23,7 +23,7 @@ public void 綁定字典() }; var builder = new ConfigurationBuilder(); - var configRoot = builder.AddInMemoryCollection(source).Build(); + var configRoot = builder.AddEnvironmentVariables().Build(); var member = configRoot.Get>(); Assert.AreEqual("9527", member["a"].Id); diff --git "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" index 4020acb1..a9c53ad4 100644 --- "a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" +++ "b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/\344\276\206\346\272\220\347\202\272\347\222\260\345\242\203\350\256\212\346\225\270.cs" @@ -20,9 +20,12 @@ public void 綁定字典() Environment.SetEnvironmentVariable("b:profile:age", "19"); Environment.SetEnvironmentVariable("b:profile:address", "Taipei"); var builder = new ConfigurationBuilder(); - var configRoot = builder.AddInMemoryCollection() + var configRoot = builder.AddEnvironmentVariables() .Build(); var member = configRoot.Get>(); + + Assert.AreEqual("9527", member["a"].Id); + Assert.AreEqual("9528", member["b"].Id); } [TestMethod] @@ -37,10 +40,9 @@ public void 綁定集合() Environment.SetEnvironmentVariable("b:profile:age", "19"); Environment.SetEnvironmentVariable("b:profile:address", "Taipei"); var builder = new ConfigurationBuilder(); - var configRoot = builder.AddInMemoryCollection() + var configRoot = builder.AddEnvironmentVariables() .Build(); var member = configRoot.Get>(); - var member2 = configRoot.Get>(); Assert.AreEqual("9527", member[0].Id); Assert.AreEqual("9528", member[1].Id); @@ -55,8 +57,7 @@ public void 綁定複雜型別() Environment.SetEnvironmentVariable("profile:address", "Taipei"); var builder = new ConfigurationBuilder(); - builder.AddEnvironmentVariables(); - var configRoot = builder.AddInMemoryCollection() + var configRoot = builder.AddEnvironmentVariables() .Build(); var member = configRoot.Get(); Assert.AreEqual("9527", member.Id); From d01f344d4c32ba5042e5dcd4801203fe55b66607 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 22:56:35 +0800 Subject: [PATCH 146/267] fix bug --- .../Lab.Config/AspNetCore3/AspNetCore3.csproj | 40 +++--- .../Controllers/DefaultController.cs | 10 +- .../Controllers/WeatherForecastController.cs | 20 ++- .../NetCore/Lab.Config/AspNetCore3/Program.cs | 40 +++--- .../AspNetCore3/ServiceCollectionEx.cs | 3 +- .../NetCore/Lab.Config/AspNetCore3/Startup.cs | 22 ++-- .../AspNetCore3/StartupInjectionAppSetting.cs | 14 +- .../StartupInjectionIOptionsMonitor.cs | 4 +- .../AspNetCore3/StartupInjectionOptions.cs | 2 +- .../StartupInjectionOptionsSnapshot.cs | 6 +- .../Lab.Config/AspNetCore3/appsettings.json | 2 - .../Lab.Config/AspNetCore5/AspNetCore5.csproj | 8 +- .../Controllers/DefaultController.cs | 2 +- .../Controllers/WeatherForecastController.cs | 2 +- .../NetCore/Lab.Config/AspNetCore5/Program.cs | 2 +- .../NetCore/Lab.Config/AspNetCore5/Startup.cs | 6 +- .../Lab.Config/AspNetCore5/appsettings.json | 2 - .../Lab.Config/ConsoleApp1/ConsoleApp1.csproj | 8 -- .../NetCore/Lab.Config/ConsoleApp1/Program.cs | 12 -- .../Properties/launchSettings.json | 8 -- .../Lab.Config/Lab.Infra/ConnectionStrings.cs | 2 +- .../Lab.Config/Lab.Infra/Lab.Infra.csproj | 24 ++-- .../NetCore/Lab.Config/Lab.Infra/Player.cs | 2 +- .../Lab.Config/MsUnitTest/MsUnitTest.csproj | 66 +++++----- .../Lab.Config/MsUnitTest/UnitTest1.cs | 14 +- .../NetFx48/AppWorkFlowWithOption.cs | 2 +- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 78 +++++------ ...yEnvironmentVariablesConfigurationTests.cs | 2 +- .../NetFx48/SurveyJsonConfigurationTests.cs | 124 +++++++++++------- .../SurveyKeyPerFileConfigurationTests.cs | 2 +- .../Lab.Config/NetFx48/SurveyOptionTests.cs | 42 +++--- .../NetFx48/SurveyUserSecretTests.cs | 4 +- .../Lab.Config/NetFx48/appsettings.QA.json | 1 - .../Lab.Config/NetFx48/appsettings.ini | 6 +- .../Lab.Config/NetFx48/appsettings.json | 2 +- .../Lab.Config/NetFx48/appsettings.test.json | 1 - .../Lab.Config/NetFx48/appsettings.xml | 4 +- 37 files changed, 290 insertions(+), 299 deletions(-) delete mode 100644 Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj delete mode 100644 Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs delete mode 100644 Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj b/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj index 3305cc3f..24827e37 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj @@ -1,29 +1,29 @@ - - netcoreapp3.1 - Debug;Release;QA - AnyCPU - + + netcoreapp3.1 + Debug;Release;QA + AnyCPU + - - true - false - + + true + false + - - - + + + - - - + + + - - - Always - - + + + Always + + diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs index 0ad61e02..170d017e 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs @@ -13,7 +13,7 @@ public class DefaultController : ControllerBase public IActionResult Get() { var serviceProvider = this.HttpContext.RequestServices; - var options = serviceProvider.GetService>(); + var options = serviceProvider.GetService>(); return this.Ok(options?.Value); } @@ -21,8 +21,8 @@ public IActionResult Get() public IActionResult GetMonitorPlayer(int id) { var serviceProvider = this.HttpContext.RequestServices; - var playerOption = serviceProvider.GetService>(); - var player = playerOption.Get($"Player{id}"); + var playerOption = serviceProvider.GetService>(); + var player = playerOption.Get($"Player{id}"); return this.Ok(player); } @@ -30,8 +30,8 @@ public IActionResult GetMonitorPlayer(int id) public IActionResult GetSnapshotPlayer(int id) { var serviceProvider = this.HttpContext.RequestServices; - var playerOption = serviceProvider.GetService>(); - var player = playerOption.Get($"Player{id}"); + var playerOption = serviceProvider.GetService>(); + var player = playerOption.Get($"Player{id}"); return this.Ok(player); } } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs index 5b4654df..55424a39 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Lab.Infra; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -20,10 +19,10 @@ public class WeatherForecastController : ControllerBase }; private readonly ILogger _logger; - private AppSetting _appSetting; - private IConfiguration _config; - private Player _player1; - private Player _player2; + private AppSetting _appSetting; + private IConfiguration _config; + private Player _player1; + private Player _player2; // TODO:依賴 AppSetting // public WeatherForecastController(AppSetting appSetting) @@ -34,7 +33,6 @@ public class WeatherForecastController : ControllerBase // TODO:依賴 IOptions public WeatherForecastController(IOptions options) { - try { this._appSetting = options.Value; @@ -83,13 +81,13 @@ public WeatherForecastController(IOptions options) [HttpGet] public IEnumerable Get() - { - var rng = new Random(); + { + var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { - Date = DateTime.Now.AddDays(index), + Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] + Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs index 83e04777..242d45b9 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs @@ -1,34 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; namespace AspNetCore3 { public class Program { + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureAppConfiguration(p => + { + p.AddJsonFile("appsettings.json", false, false); + }); + webBuilder.UseStartup(); + + // webBuilder.UseStartup(); + // webBuilder.UseStartup(); + // webBuilder.UseStartup(); + // webBuilder.UseStartup(); + }); + } + public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureAppConfiguration(p => - { - p.AddJsonFile("appsettings.json", false, false); - }); - webBuilder.UseStartup(); - // webBuilder.UseStartup(); - // webBuilder.UseStartup(); - // webBuilder.UseStartup(); - // webBuilder.UseStartup(); - }); } -} +} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs index 907ebd43..a1adff51 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs @@ -32,4 +32,5 @@ // return config; // } // } -// } \ No newline at end of file +// } + diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs index 5faec907..d4468799 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs @@ -38,23 +38,23 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - // AppSetting + //驗證 AppSetting services.AddOptions() .ValidateDataAnnotations() .Validate(p => - { - if (p.AllowedHosts == null) - { - return false; - } + { + if (p.AllowedHosts == null) + { + return false; + } - return true; - }, "AllowedHosts must be value"); // Failure message. + return true; + }, "AllowedHosts must be value"); // Failure message. - //`J Options M IConfiguration + //注入 Options 和完整 IConfiguration services.Configure(this.Configuration); - - //`J Options M Configuration Section Name + + //注入 Options 和 Configuration Section Name // services.Configure("Player1", this.Configuration.GetSection("Player1")); // services.Configure("Player2", this.Configuration.GetSection("Player2")); // services.Configure("Player3", this.Configuration.GetSection("Player3")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs index 9c6d3481..311eda8e 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs @@ -38,14 +38,14 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J AppSetting + //注入 AppSetting services.AddSingleton(provider => - { - //lazy load - var appSetting = new AppSetting(); - this.Configuration.Bind(appSetting); - return appSetting; - }); + { + //lazy load + var appSetting = new AppSetting(); + this.Configuration.Bind(appSetting); + return appSetting; + }); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs index 97d60096..5a43db3c 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionIOptionsMonitor.cs @@ -38,10 +38,10 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J Options M IConfiguration + //注入 Options 和完整 IConfiguration services.Configure(this.Configuration); - //`J Options M Configuration Section Name + //注入 Options 和 Configuration Section Name services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs index 178ddab3..f953b4ce 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs @@ -38,7 +38,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //`J Options M IConfiguration + //注入 Options 和完整 IConfiguration services.Configure(this.Configuration); } } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs index 44f45b23..7d983108 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs @@ -38,7 +38,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - // AppSetting + //驗證 AppSetting services.AddOptions() .ValidateDataAnnotations() .Validate(p => @@ -51,10 +51,10 @@ public void ConfigureServices(IServiceCollection services) return true; }, "AllowedHosts must be value"); // Failure message. - //`J Options M IConfiguration + //注入 Options 和完整 IConfiguration services.Configure(this.Configuration); - //`J Options M Configuration Section Name + //注入 Options 和 Configuration Section Name services.Configure(this.Configuration); services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json index 829fc2f8..c03edfe4 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json @@ -7,7 +7,6 @@ } }, "AllowedHosts": "*", - "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;", "AuthenticationConnectionString": "" @@ -20,7 +19,6 @@ "AppId": "player1", "Key": "12345678990" }, - "Player2": { "AppId": "player2", "Key": "player2_123456" diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj index 60048b36..92063f64 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj @@ -7,13 +7,13 @@ - - - + + + - + diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs index 3f0d4c4b..9e817d64 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs @@ -45,7 +45,7 @@ public IActionResult GetMonitorPlayer(int id) }; appSettingOptions.OnChange(p => { - Console.WriteLine("`Iwܧ"); + Console.WriteLine("節點已變更"); }); return this.Ok(content); } diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs index 932d39a7..dab91d80 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/WeatherForecastController.cs @@ -1,4 +1,4 @@ -// using System; +// using System; // using System.Collections.Generic; // using System.Linq; // using System.Threading.Tasks; diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs index 861aa8ce..1fc668cf 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs @@ -12,7 +12,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) { webBuilder.ConfigureAppConfiguration(p => { - // sJպA + // 不重新載入組態 //p.AddJsonFile("appsettings.json", false, false); }); webBuilder.UseStartup(); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs index 1a7bd75c..78d69940 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs @@ -45,7 +45,7 @@ public void ConfigureServices(IServiceCollection services) c.SwaggerDoc("v1", new OpenApiInfo {Title = "AspNetCore5", Version = "v1"}); }); - // AppSetting + //驗證 AppSetting services.AddOptions() .ValidateDataAnnotations() .Validate(p => @@ -58,10 +58,10 @@ public void ConfigureServices(IServiceCollection services) return true; }, "AllowedHosts must be value"); // Failure message. - //`J Options M IConfiguration + //注入 Options 和完整 IConfiguration services.Configure(this.Configuration); - //`J Options M Configuration Section Name + //注入 Options 和 Configuration Section Name services.Configure("Player1", this.Configuration.GetSection("Player1")); services.Configure("Player2", this.Configuration.GetSection("Player2")); services.Configure("Player3", this.Configuration.GetSection("Player3")); diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json index ee3bc5a4..25a5a48f 100644 --- a/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json +++ b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json @@ -7,7 +7,6 @@ } }, "AllowedHosts": "*", - "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;", "AuthenticationConnectionString": "" @@ -20,7 +19,6 @@ "AppId": "player1", "Key": "12345678990" }, - "Player2": { "AppId": "player2", "Key": "player2_123456" diff --git a/Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj b/Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj deleted file mode 100644 index 9590466a..00000000 --- a/Configuration/NetCore/Lab.Config/ConsoleApp1/ConsoleApp1.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net5.0 - - - diff --git a/Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs b/Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs deleted file mode 100644 index be1b5acd..00000000 --- a/Configuration/NetCore/Lab.Config/ConsoleApp1/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ConsoleApp1 -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json deleted file mode 100644 index 053b519b..00000000 --- a/Configuration/NetCore/Lab.Config/ConsoleApp1/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "ConsoleApp1": { - "commandName": "Project", - "commandLineArgs": "-" - } - } -} \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/Lab.Infra/ConnectionStrings.cs b/Configuration/NetCore/Lab.Config/Lab.Infra/ConnectionStrings.cs index 45b7fe3e..bd90c3ca 100644 --- a/Configuration/NetCore/Lab.Config/Lab.Infra/ConnectionStrings.cs +++ b/Configuration/NetCore/Lab.Config/Lab.Infra/ConnectionStrings.cs @@ -1,4 +1,4 @@ -namespace Lab.Infra +namespace Lab.Infra { public class ConnectionStrings { diff --git a/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj b/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj index e326d242..fc35969d 100644 --- a/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj +++ b/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj @@ -1,18 +1,18 @@ - - netcoreapp3.1 - Debug;Release;QA - AnyCPU - + + netcoreapp3.1 + Debug;Release;QA + AnyCPU + - - true - false - + + true + false + - - - + + + diff --git a/Configuration/NetCore/Lab.Config/Lab.Infra/Player.cs b/Configuration/NetCore/Lab.Config/Lab.Infra/Player.cs index 9c2a26ea..2ef82d7a 100644 --- a/Configuration/NetCore/Lab.Config/Lab.Infra/Player.cs +++ b/Configuration/NetCore/Lab.Config/Lab.Infra/Player.cs @@ -1,4 +1,4 @@ -namespace Lab.Infra +namespace Lab.Infra { public class Player { diff --git a/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj b/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj index b37edb3d..e3c2748e 100644 --- a/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj +++ b/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj @@ -1,37 +1,37 @@ - - netcoreapp3.1 - - false - - Debug;Release;QA - - AnyCPU - - - - true - false - - - - - - - - - - - - - - - - - - Always - - + + netcoreapp3.1 + + false + + Debug;Release;QA + + AnyCPU + + + + true + false + + + + + + + + + + + + + + + + + + Always + + diff --git a/Configuration/NetCore/Lab.Config/MsUnitTest/UnitTest1.cs b/Configuration/NetCore/Lab.Config/MsUnitTest/UnitTest1.cs index fc3a8923..42df9474 100644 --- a/Configuration/NetCore/Lab.Config/MsUnitTest/UnitTest1.cs +++ b/Configuration/NetCore/Lab.Config/MsUnitTest/UnitTest1.cs @@ -10,7 +10,7 @@ namespace MsUnitTest public class UnitTest1 { [TestMethod] - public void zLAppSettingŪ]w() + public void 透過AppSetting物件讀取設定檔() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -24,7 +24,7 @@ public class UnitTest1 } [TestMethod] - public void zLAppSettingŪ]w_ϬqsbߥXҥ~() + public void 透過AppSetting物件讀取設定檔_區段不存在拋出例外() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -38,7 +38,7 @@ public class UnitTest1 } [TestMethod] - public void jw]w_XRk_Get() + public void 綁定設定_擴充方法_Get() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -50,7 +50,7 @@ public class UnitTest1 } [TestMethod] - public void jw]w_XRk_Bind() + public void 綁定設定_擴充方法_Bind() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -64,7 +64,7 @@ public class UnitTest1 } [TestMethod] - public void Ū]w() + public void 讀取設定檔() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -78,7 +78,7 @@ public class UnitTest1 } [TestMethod] - public void Ū]w_GetConnectionString() + public void 讀取設定檔_GetConnectionString() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -92,7 +92,7 @@ public class UnitTest1 } [TestMethod] - public void Ū]w_TryGet() + public void 讀取設定檔_TryGet() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs index 6dd2db39..8fa36c8b 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Options; namespace NetFx48 diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index c347c8c3..c31a487c 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -10,56 +10,56 @@ Debug;Release;QA AnyCPU - 659be13b-676e-4c9e-a0b9-0df2ffd75cfc - + 659be13b-676e-4c9e-a0b9-0df2ffd75cfc + - true - false + true + false - - - - - - - - - - - - - + + + + + + + + + + + + + - - true - Always - PreserveNewest - - - true - Always - PreserveNewest - + + true + Always + PreserveNewest + + + true + Always + PreserveNewest + - - Always - - - Always - - - Always - - - Always - + + Always + + + Always + + + Always + + + Always + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs index bf710645..773448a6 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index 05f034b6..fee31ec7 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -17,7 +17,7 @@ public void 切換組態() { string environmentName; #if DEBUG - environmentName = "Development"; + environmentName = "Development"; #elif QA environmentName = "QA"; #elif STAGING @@ -27,8 +27,8 @@ public void 切換組態() #endif var configBuilder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", false, true) - .AddJsonFile($"appsettings.{environmentName}.json", true, true) + .AddJsonFile("appsettings.json", false, true) + .AddJsonFile($"appsettings.{environmentName}.json", true, true) ; var configRoot = configBuilder.Build(); @@ -60,31 +60,31 @@ public void 注入Configuration() { var builder = Host.CreateDefaultBuilder(null) .ConfigureAppConfiguration(config => - { - config.Sources.Clear(); - config.AddJsonFile("appsettings.json", true, true); - }) + { + config.Sources.Clear(); + config.AddJsonFile("appsettings.json", true, true); + }) .ConfigureServices(service => - { - //DI - service.AddScoped(typeof(AppWorkFlow)); - }); + { + //DI + service.AddScoped(typeof(AppWorkFlow)); + }); var host = builder.Build(); var appService = host.Services.GetService(); - var playerId = appService.GetPlayerId(); + var playerId = appService.GetPlayerId(); Console.WriteLine($"AppId = {playerId}"); } [TestMethod] - public void 記憶體組態() + public void 讀取記憶體組態() { var configBuilder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddInMemoryCollection(new Dictionary { - {"Player:AppId", "player1"}, - {"Player:Key", "1234567890"}, + { "Player:AppId", "player1" }, + { "Player:Key", "1234567890" }, { "ConnectionStrings:DefaultConnectionString", "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" @@ -102,6 +102,32 @@ public void 記憶體組態() Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); } + [TestMethod] + public void 讀取記憶體組態後綁定() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddInMemoryCollection(new Dictionary + { + { "Player:AppId", "player1" }, + { "Player:Key", "1234567890" }, + { + "ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var configRoot = configBuilder.Build(); + var appSetting = configRoot.Get(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSetting.Player.AppId}"); + Console.WriteLine($"Key = {appSetting.Player.Key}"); + Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); + } + [TestMethod] public void 通過Host() { @@ -113,8 +139,8 @@ public void 實例化JsonConfigurationProvider() { var configProvider = new JsonConfigurationProvider(new JsonConfigurationSource { - Optional = false, - Path = "appsettings.json", + Optional = false, + Path = "appsettings.json", ReloadOnChange = true }); configProvider.Load(); @@ -122,27 +148,13 @@ public void 實例化JsonConfigurationProvider() Console.WriteLine($"AppId = {appId}"); } - [TestMethod] - public void 讀取設定檔_GetSection() - { - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json"); - var configRoot = builder.Build(); - - Console.WriteLine($"AppId = {configRoot.GetSection("AppId")}"); - Console.WriteLine($"AppId = {configRoot.GetSection("Player:AppId")}"); - Console.WriteLine($"Key = {configRoot.GetSection("Player:Key")}"); - Console.WriteLine($"Connection String = {configRoot.GetSection("ConnectionStrings:DefaultConnectionString")}"); - } - [TestMethod] public void 讀取設定檔_GetChild() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var configRoot = builder.Build(); + var configRoot = builder.Build(); var firstSections = configRoot.GetChildren(); foreach (var firstSection in firstSections) { @@ -154,6 +166,20 @@ public void 讀取設定檔_GetChild() } } + [TestMethod] + public void 讀取設定檔_GetSection() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); + var configRoot = builder.Build(); + + Console.WriteLine($"AppId = {configRoot.GetSection("AppId")}"); + Console.WriteLine($"AppId = {configRoot.GetSection("Player:AppId")}"); + Console.WriteLine($"Key = {configRoot.GetSection("Player:Key")}"); + Console.WriteLine($"Connection String = {configRoot.GetSection("ConnectionStrings:DefaultConnectionString")}"); + } + [TestMethod] public void 讀取設定檔_TryGet() { @@ -191,8 +217,8 @@ public void 讀取設定檔_綁定_Get() var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); - var configRoot = builder.Build(); - var player = configRoot.GetSection("Player").Get(); + var configRoot = builder.Build(); + var player = configRoot.GetSection("Player").Get(); var appSetting = configRoot.Get(); Console.WriteLine($"AppId = {player.AppId}"); @@ -204,22 +230,22 @@ private static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration(config => - { - config.Sources.Clear(); - config.AddJsonFile("appsettings.json", true, true); - var configRoot = config.Build(); - - //讀取組態 - Console.WriteLine($"AppId = {configRoot["AppId"]}"); - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console - .WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); - }) + { + config.Sources.Clear(); + config.AddJsonFile("appsettings.json", true, true); + var configRoot = config.Build(); + + //讀取組態 + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console + .WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + }) .ConfigureServices(service => - { - //DI - }); + { + //DI + }); } } } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs index 5205b0f4..573309f1 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyKeyPerFileConfigurationTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs index 6845a811..96824af4 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs @@ -10,12 +10,12 @@ namespace NetFx48 public class SurveyOptionTests { [TestMethod] - public void `JOption() + public void 注入Option() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => { - // 1.ŪպA + // 1.讀組態檔 var environmentName = hosting.Configuration["ENVIRONMENT2"]; configBuilder.AddJsonFile("appsettings.json", false, true); @@ -25,10 +25,10 @@ public class SurveyOptionTests }) .ConfigureServices((hosting, services) => { - // 2. `J Option M Configuration + // 2. 注入 Option 和 Configuration services.Configure(hosting.Configuration); - //`JLA + //注入其他服務 services.AddSingleton(); }) ; @@ -39,12 +39,12 @@ public class SurveyOptionTests } [TestMethod] - public void `JOptionMonitor() + public void 注入OptionMonitor() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => { - // 1.ŪպA + // 1.讀組態檔 var environmentName = hosting.Configuration["ENVIRONMENT2"]; configBuilder.AddJsonFile("appsettings.json", false, true); @@ -54,14 +54,14 @@ public class SurveyOptionTests }) .ConfigureServices((hosting, services) => { - // `J Option M Configuration + // 注入 Option 和完整 Configuration services.Configure(hosting.Configuration); - // `J Option MSw Configuration Section Name + // 注入 Option 和特定 Configuration Section Name services.Configure("Player", hosting.Configuration.GetSection("Player")); - //`JLA + //注入其他服務 services.AddScoped(); }) ; @@ -72,7 +72,7 @@ public class SurveyOptionTests } [TestMethod] - public void `JOptionSnapshot() + public void 注入OptionSnapshot() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => @@ -86,14 +86,14 @@ public class SurveyOptionTests }) .ConfigureServices((hosting, services) => { - // `J Option by պA + // 注入 Option by 完整組態 services.Configure(hosting.Configuration); - // `J Option by SwպA + // 注入 Option by 特定組態 services.Configure(hosting.Configuration .GetSection("Player")); - //`JLA + //注入其他服務 services.AddScoped(); }) ; @@ -104,12 +104,12 @@ public class SurveyOptionTests } [TestMethod] - public void () + public void 驗證() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => { - // 1.ŪպA + // 1.讀組態檔 var environmentName = hosting.Configuration["ENVIRONMENT2"]; configBuilder.AddJsonFile("appsettings.json", false, true); @@ -119,9 +119,9 @@ public class SurveyOptionTests }) .ConfigureServices((hosting, services) => { - // 2. `J Option M Configuration + // 2. 注入 Option 和 Configuration services.Configure(hosting.Configuration); - // + //驗證 services.AddOptions() .ValidateDataAnnotations() .Validate(p => @@ -137,7 +137,7 @@ public class SurveyOptionTests "DefaultConnectionString must be value"); // Failure message. ; - //`JLA + //注入其他服務 services.AddSingleton(); }) ; @@ -148,12 +148,12 @@ public class SurveyOptionTests } [TestMethod] - public void `JպA() + public void 直接注入組態物件() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => { - // 1.ŪպA + // 1.讀組態檔 var environmentName = hosting.Configuration["ENVIRONMENT2"]; configBuilder.AddJsonFile("appsettings.json", false, true); @@ -166,7 +166,7 @@ public class SurveyOptionTests var appSetting = hosting.Configuration.Get(); services.AddSingleton(typeof(AppSetting), appSetting); - //`JLA + //注入其他服務 services.AddSingleton(); }) ; diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs index 67e005f8..32043141 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs @@ -11,7 +11,7 @@ namespace NetFx48 public class SurveyUserSecretTests { [TestMethod] - public void HostŪK() + public void Host讀取秘密() { var builder = Host.CreateDefaultBuilder() .ConfigureHostConfiguration(config => @@ -27,7 +27,7 @@ public class SurveyUserSecretTests } [TestMethod] - public void ʹҤƲպAŪK() + public void 手動實例化組態讀取秘密() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json index 077edc27..b12c223d 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json @@ -2,7 +2,6 @@ "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ConsoleApp.NewDb.QA;Trusted_Connection=True;" }, - "Player": { "AppId": "qa", "Key": "qa1234567890" diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini index c7092637..2b9d12b9 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini @@ -1,6 +1,6 @@ [ConnectionStrings] -DefaultConnectionString="Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" +DefaultConnectionString = "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" [Player] -AppId=testApp -Key=12345678990 +AppId = testApp +Key = 12345678990 diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json index 52fc1bcc..20f51da4 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json @@ -7,5 +7,5 @@ "Key": "1234567890" }, "Environment": "Development", - "ApplicationName":"NetFx48" + "ApplicationName": "NetFx48" } \ No newline at end of file diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json index c339236f..420fcb73 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json @@ -2,7 +2,6 @@ "ConnectionStrings": { "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb.Test;Trusted_Connection=True;" }, - "Player": { "AppId": "test", "Key": "test1234567890" diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml index adf9ceb9..80f4425b 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml +++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml @@ -1,7 +1,9 @@  - Server=(localdb)\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True; + + Server=(localdb)\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True; + testApp From ca4bb98b6601ecb6d78d536c3c73a31dd0d9b34b Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 23:21:17 +0800 Subject: [PATCH 147/267] refactor --- .../NetCore/Lab.Config/NetFx48/NetFx48.csproj | 26 ++-- .../NetFx48/SurveyJsonConfigurationTests.cs | 53 ------- .../NetFx48/SurveyMemoryConfigurationTests.cs | 130 ++++++++++++++++++ 3 files changed, 143 insertions(+), 66 deletions(-) create mode 100644 Configuration/NetCore/Lab.Config/NetFx48/SurveyMemoryConfigurationTests.cs diff --git a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj index c31a487c..92ceb1d4 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj +++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj @@ -19,19 +19,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs index fee31ec7..8118d15a 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; @@ -76,58 +75,6 @@ public void 注入Configuration() Console.WriteLine($"AppId = {playerId}"); } - [TestMethod] - public void 讀取記憶體組態() - { - var configBuilder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddInMemoryCollection(new Dictionary - { - { "Player:AppId", "player1" }, - { "Player:Key", "1234567890" }, - { - "ConnectionStrings:DefaultConnectionString", - "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" - }, - }) - ; - - var configRoot = configBuilder.Build(); - - //讀取組態 - - Console.WriteLine($"AppId = {configRoot["AppId"]}"); - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); - } - - [TestMethod] - public void 讀取記憶體組態後綁定() - { - var configBuilder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddInMemoryCollection(new Dictionary - { - { "Player:AppId", "player1" }, - { "Player:Key", "1234567890" }, - { - "ConnectionStrings:DefaultConnectionString", - "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" - }, - }) - ; - - var configRoot = configBuilder.Build(); - var appSetting = configRoot.Get(); - - //讀取組態 - - Console.WriteLine($"AppId = {appSetting.Player.AppId}"); - Console.WriteLine($"Key = {appSetting.Player.Key}"); - Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); - } - [TestMethod] public void 通過Host() { diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyMemoryConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyMemoryConfigurationTests.cs new file mode 100644 index 00000000..8aba7974 --- /dev/null +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyMemoryConfigurationTests.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace NetFx48 +{ + [TestClass] + public class SurveyMemoryConfigurationTests + { + [TestMethod] + public void 讀取記憶體組態() + { + var configBuilder = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Player:AppId", "player1" }, + { "Player:Key", "1234567890" }, + { + "ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var configRoot = configBuilder.Build(); + + //讀取組態 + + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + } + + [TestMethod] + public void 讀取記憶體組態_綁定() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddInMemoryCollection(new Dictionary + { + { "Player:AppId", "player1" }, + { "Player:Key", "1234567890" }, + { + "ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var configRoot = configBuilder.Build(); + var appSetting = configRoot.Get(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSetting.Player.AppId}"); + Console.WriteLine($"Key = {appSetting.Player.Key}"); + Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); + } + + [TestMethod] + public void 讀取記憶體組態_綁定_集合() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddInMemoryCollection(new Dictionary + { + { "a:Player:AppId", "player1" }, + { "a:Player:Key", "1234567890" }, + { + "a:ConnectionStrings:DefaultConnectionString", + "a:Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + { "b:Player:AppId", "player2" }, + { "b:Player:Key", "1234567890" }, + { + "b:ConnectionStrings:DefaultConnectionString", + "b:Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var configRoot = configBuilder.Build(); + var appSettings = configRoot.Get>(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSettings[0].Player.AppId}"); + Console.WriteLine($"Key = {appSettings[0].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings[0].ConnectionStrings.DefaultConnectionString}"); + } + + [TestMethod] + public void 讀取記憶體組態_綁定_字典() + { + var configBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddInMemoryCollection(new Dictionary + { + { "a:Player:AppId", "player1" }, + { "a:Player:Key", "1234567890" }, + { + "a:ConnectionStrings:DefaultConnectionString", + "a:Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + { "b:Player:AppId", "player2" }, + { "b:Player:Key", "1234567890" }, + { + "b:ConnectionStrings:DefaultConnectionString", + "b:Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;" + }, + }) + ; + + var configRoot = configBuilder.Build(); + var appSettings = configRoot.Get>(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSettings["a"].Player.AppId}"); + Console.WriteLine($"Key = {appSettings["a"].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings["a"].ConnectionStrings.DefaultConnectionString}"); + Console.WriteLine($"AppId = {appSettings["b"].Player.AppId}"); + Console.WriteLine($"Key = {appSettings["b"].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings["b"].ConnectionStrings.DefaultConnectionString}"); + } + } +} \ No newline at end of file From fee8cedd52959a956045bc4ef6873e35e871903c Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 19 Jan 2022 23:43:25 +0800 Subject: [PATCH 148/267] add test case --- ...yEnvironmentVariablesConfigurationTests.cs | 173 ++++++++++++++---- 1 file changed, 134 insertions(+), 39 deletions(-) diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs index 773448a6..aed350b8 100644 --- a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs +++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -15,25 +16,25 @@ public void Host實例化ConfigurationBuilder() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => - { - // config.Sources.Clear(); - var hostingEnvironmentEnvironmentName = - hosting.HostingEnvironment.EnvironmentName; - configBuilder.AddEnvironmentVariables("Custom_"); - var configRoot = configBuilder.Build(); - - //讀取組態 - Console - .WriteLine($"ASPNETCORE_ENVIRONMENT = {configRoot["ASPNETCORE_ENVIRONMENT"]}"); - Console - .WriteLine($"DOTNET_ENVIRONMENT = {configRoot["DOTNET_ENVIRONMENT"]}"); - Console - .WriteLine($"CUSTOM_ENVIRONMENT = {configRoot["CUSTOM_ENVIRONMENT"]}"); - Console - .WriteLine($"ENVIRONMENT1 = {configRoot["ENVIRONMENT1"]}"); - }) + { + // config.Sources.Clear(); + var hostingEnvironmentEnvironmentName = + hosting.HostingEnvironment.EnvironmentName; + configBuilder.AddEnvironmentVariables("Custom_"); + var configRoot = configBuilder.Build(); + + //讀取組態 + Console + .WriteLine($"ASPNETCORE_ENVIRONMENT = {configRoot["ASPNETCORE_ENVIRONMENT"]}"); + Console + .WriteLine($"DOTNET_ENVIRONMENT = {configRoot["DOTNET_ENVIRONMENT"]}"); + Console + .WriteLine($"CUSTOM_ENVIRONMENT = {configRoot["CUSTOM_ENVIRONMENT"]}"); + Console + .WriteLine($"ENVIRONMENT1 = {configRoot["ENVIRONMENT1"]}"); + }) ; - var host = builder.Build(); + var host = builder.Build(); var environment = host.Services.GetRequiredService(); Console.WriteLine($"EnvironmentName={environment.EnvironmentName}"); } @@ -43,23 +44,23 @@ public void 切換組態設定() { var builder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hosting, configBuilder) => - { - // config.Sources.Clear(); - var environmentName = - hosting.Configuration["ENVIRONMENT2"]; - configBuilder.AddJsonFile("appsettings.json", false, true); - configBuilder - .AddJsonFile($"appsettings.{environmentName}.json", - true, true); - - var configRoot = configBuilder.Build(); - - //讀取組態 - Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); - Console.WriteLine($"Key = {configRoot["Player:Key"]}"); - Console - .WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); - }) + { + // config.Sources.Clear(); + var environmentName = + hosting.Configuration["ENVIRONMENT2"]; + configBuilder.AddJsonFile("appsettings.json", false, true); + configBuilder + .AddJsonFile($"appsettings.{environmentName}.json", + true, true); + + var configRoot = configBuilder.Build(); + + //讀取組態 + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console + .WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + }) ; builder.Build(); } @@ -83,14 +84,108 @@ public void 設定主機組態() { var builder = Host.CreateDefaultBuilder() .ConfigureHostConfiguration(config => - { - config.AddJsonFile("appsettings.json", false, true); - }) + { + config.AddJsonFile("appsettings.json", false, true); + }) ; - var host = builder.Build(); + var host = builder.Build(); var environment = host.Services.GetRequiredService(); Console.WriteLine($"EnvironmentName={environment.EnvironmentName}"); } + + [TestMethod] + public void 讀取環境變數() + { + Environment.SetEnvironmentVariable("Player:AppId", "player1"); + Environment.SetEnvironmentVariable("Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + var configBuilder = new ConfigurationBuilder().AddEnvironmentVariables(); + + var configRoot = configBuilder.Build(); + + //讀取組態 + + Console.WriteLine($"AppId = {configRoot["AppId"]}"); + Console.WriteLine($"AppId = {configRoot["Player:AppId"]}"); + Console.WriteLine($"Key = {configRoot["Player:Key"]}"); + Console.WriteLine($"Connection String = {configRoot["ConnectionStrings:DefaultConnectionString"]}"); + } + + + [TestMethod] + public void 讀取環境變數_綁定() + { + Environment.SetEnvironmentVariable("Player:AppId", "player1"); + Environment.SetEnvironmentVariable("Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + var configBuilder = new ConfigurationBuilder().AddEnvironmentVariables(); + + var configRoot = configBuilder.Build(); + var appSetting = configRoot.Get(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSetting.Player.AppId}"); + Console.WriteLine($"Key = {appSetting.Player.Key}"); + Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}"); + } + + [TestMethod] + public void 讀取環境變數_綁定_集合() + { + Environment.SetEnvironmentVariable("a:Player:AppId", "player1"); + Environment.SetEnvironmentVariable("a:Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("a:ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + Environment.SetEnvironmentVariable("b:Player:AppId", "player2"); + Environment.SetEnvironmentVariable("b:Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("b:ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + + var configBuilder = new ConfigurationBuilder().AddEnvironmentVariables(); + + var configRoot = configBuilder.Build(); + var appSettings = configRoot.Get>(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSettings[0].Player.AppId}"); + Console.WriteLine($"Key = {appSettings[0].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings[0].ConnectionStrings.DefaultConnectionString}"); + Console.WriteLine($"AppId = {appSettings[1].Player.AppId}"); + Console.WriteLine($"Key = {appSettings[1].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings[1].ConnectionStrings.DefaultConnectionString}"); + } + + [TestMethod] + public void 讀取環境變數_綁定_字典() + { + Environment.SetEnvironmentVariable("a:Player:AppId", "player1"); + Environment.SetEnvironmentVariable("a:Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("a:ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + Environment.SetEnvironmentVariable("b:Player:AppId", "player2"); + Environment.SetEnvironmentVariable("b:Player:Key", "1234567890"); + Environment.SetEnvironmentVariable("b:ConnectionStrings:DefaultConnectionString", + "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"); + + var configBuilder = new ConfigurationBuilder().AddEnvironmentVariables(); + + var configRoot = configBuilder.Build(); + var appSettings = configRoot.Get>(); + + //讀取組態 + + Console.WriteLine($"AppId = {appSettings["a"].Player.AppId}"); + Console.WriteLine($"Key = {appSettings["a"].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings["a"].ConnectionStrings.DefaultConnectionString}"); + Console.WriteLine($"AppId = {appSettings["b"].Player.AppId}"); + Console.WriteLine($"Key = {appSettings["b"].Player.Key}"); + Console.WriteLine($"Connection String = {appSettings["b"].ConnectionStrings.DefaultConnectionString}"); + } + } } \ No newline at end of file From 23bd21a9c729910bc8f7d39ea89799a7d83cc521 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 20 Jan 2022 00:46:40 +0800 Subject: [PATCH 149/267] feat: Env File Config --- .../Lab.EnvFileConfig.TestProject.csproj | 28 +++++++++++++++ .../UnitTest1.cs | 22 ++++++++++++ .../Lab.EnvFileConfig.TestProject/secret.env | 2 ++ .../Lab.EnvFileConfig/Lab.EnvFileConfig.sln | 22 ++++++++++++ .../EnvFileConfigurationExtensions.cs | 15 ++++++++ .../EnvFileConfigurationProvider.cs | 36 +++++++++++++++++++ .../EnvFileConfigurationSource.cs | 19 ++++++++++ .../Lab.EnvFileConfig.csproj | 15 ++++++++ .../Lab.EnvFileConfig/Program.cs | 32 +++++++++++++++++ 9 files changed, 191 insertions(+) create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/Lab.EnvFileConfig.TestProject.csproj create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/secret.env create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.sln create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationExtensions.cs create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationProvider.cs create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationSource.cs create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/Lab.EnvFileConfig.TestProject.csproj b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/Lab.EnvFileConfig.TestProject.csproj new file mode 100644 index 00000000..03a75833 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/Lab.EnvFileConfig.TestProject.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + Always + + + + diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs new file mode 100644 index 00000000..79283a3b --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs @@ -0,0 +1,22 @@ +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.EnvFileConfig.TestProject; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod1() + { + var configRoot = new ConfigurationBuilder() + + // .AddJsonFile("appSettings.json") + .AddEnvFile("secret.env") + .Build() + ; + var section = configRoot.GetSection("SQL_SERVER_CS"); + Console.WriteLine($"Value = {section.Value}"); + } +} \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/secret.env b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/secret.env new file mode 100644 index 00000000..fc2d45ae --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/secret.env @@ -0,0 +1,2 @@ +SQL_SERVER_CS=foo-bar +REDIS_ENDPOINT=localhost:6379 \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.sln b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.sln new file mode 100644 index 00000000..7a85e196 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EnvFileConfig", "Lab.EnvFileConfig\Lab.EnvFileConfig.csproj", "{2982F90A-3127-4E7E-8DC3-44512C0CE1E2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EnvFileConfig.TestProject", "Lab.EnvFileConfig.TestProject\Lab.EnvFileConfig.TestProject.csproj", "{6C8009BE-E88B-4A97-9D44-AE753BEBE2E4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2982F90A-3127-4E7E-8DC3-44512C0CE1E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2982F90A-3127-4E7E-8DC3-44512C0CE1E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2982F90A-3127-4E7E-8DC3-44512C0CE1E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2982F90A-3127-4E7E-8DC3-44512C0CE1E2}.Release|Any CPU.Build.0 = Release|Any CPU + {6C8009BE-E88B-4A97-9D44-AE753BEBE2E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C8009BE-E88B-4A97-9D44-AE753BEBE2E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C8009BE-E88B-4A97-9D44-AE753BEBE2E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C8009BE-E88B-4A97-9D44-AE753BEBE2E4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationExtensions.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationExtensions.cs new file mode 100644 index 00000000..24143051 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; + +namespace Lab.EnvFileConfig +{ + public static class EnvFileConfigurationExtensions + { + public static IConfigurationBuilder AddEnvFile(this IConfigurationBuilder builder, string envFile) + { + var source = new EnvFileConfigurationSource(envFile); + builder.Add(source); + builder.AddEnvironmentVariables(); + return builder; + } + } +} \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationProvider.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationProvider.cs new file mode 100644 index 00000000..4be70094 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationProvider.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Configuration; + +namespace Lab.EnvFileConfig +{ + public class EnvFileConfigurationProvider : ConfigurationProvider + { + private readonly string _envFile; + + public EnvFileConfigurationProvider(string envFile) + { + this._envFile = envFile; + } + + public override void Load() + { + if (!File.Exists(this._envFile)) + { + return; + } + + foreach (var line in File.ReadAllLines(this._envFile)) + { + var parts = line.Split( + '=', + StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length != 2) + { + continue; + } + + Environment.SetEnvironmentVariable(parts[0], parts[1]); + } + } + } +} \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationSource.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationSource.cs new file mode 100644 index 00000000..cc0c8e40 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/EnvFileConfigurationSource.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; + +namespace Lab.EnvFileConfig +{ + public class EnvFileConfigurationSource : IConfigurationSource + { + private readonly string _envFile; + + public EnvFileConfigurationSource(string envFile) + { + this._envFile = envFile; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new EnvFileConfigurationProvider(this._envFile); + } + } +} \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj new file mode 100644 index 00000000..f3baa1a4 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs new file mode 100644 index 00000000..c4d3d476 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs @@ -0,0 +1,32 @@ +// using System.Diagnostics; +// using Lab.EnvFileConfig; +// using Microsoft.Extensions.Configuration; +// +// namespace App +// { +// public class Program +// { +// private static void Main() +// { +// var initialSettings = new Dictionary +// { +// ["Gender"] = "Male", +// ["Age"] = "18", +// ["ContactInfo:EmailAddress"] = "foobar@outlook.com", +// ["ContactInfo:PhoneNo"] = "123456789" +// }; +// +// var profile = new ConfigurationBuilder() +// .AddJsonFile("appSettings.json") +// .AddDatabase("DefaultDb", initialSettings) +// .Build() +// .Get(); +// +// Debug.Assert(profile.Gender == Gender.Male); +// Debug.Assert(profile.Age == 18); +// Debug.Assert(profile.ContactInfo.EmailAddress == "foobar@outlook.com"); +// Debug.Assert(profile.ContactInfo.PhoneNo == "123456789"); +// } +// } +// } + From 9a0ea0cc19b189309d50bb856a34634810bfc7f3 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 20 Jan 2022 17:50:50 +0800 Subject: [PATCH 150/267] refactor --- .../Lab.EnvFileConfig.TestProject/UnitTest1.cs | 15 ++++++++++++++- .../Lab.EnvFileConfig/AppSetting.cs | 8 ++++++++ .../Lab.EnvFileConfig/Lab.EnvFileConfig.csproj | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/AppSetting.cs diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs index 79283a3b..27513906 100644 --- a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,7 +9,7 @@ namespace Lab.EnvFileConfig.TestProject; public class UnitTest1 { [TestMethod] - public void TestMethod1() + public void 讀取ENV檔案() { var configRoot = new ConfigurationBuilder() @@ -19,4 +20,16 @@ public void TestMethod1() var section = configRoot.GetSection("SQL_SERVER_CS"); Console.WriteLine($"Value = {section.Value}"); } + + [TestMethod] + public void 讀取ENV檔案後綁定() + { + var configRoot = new ConfigurationBuilder() + .AddEnvFile("secret.env") + .Build() + ; + var appSetting = configRoot.Get(); + Assert.AreEqual("foo-bar", appSetting.SQL_SERVER_CS); + Assert.AreEqual("localhost:6379", appSetting.REDIS_ENDPOINT); + } } \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/AppSetting.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/AppSetting.cs new file mode 100644 index 00000000..78d54413 --- /dev/null +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/AppSetting.cs @@ -0,0 +1,8 @@ +namespace Lab.EnvFileConfig; + +public class AppSetting +{ + public string SQL_SERVER_CS { get; set; } + + public string REDIS_ENDPOINT { get; set; } +} \ No newline at end of file diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj index f3baa1a4..a7cfe4a7 100644 --- a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj @@ -9,6 +9,7 @@ + From c1dd221ac41b123e46f4c6f9b5ff022774b9f6cd Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 20 Jan 2022 19:12:23 +0800 Subject: [PATCH 151/267] refactor --- .../Lab.EnvFileConfig.csproj | 8 ++--- .../Lab.EnvFileConfig/Program.cs | 32 ------------------- 2 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj index a7cfe4a7..48c93535 100644 --- a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj +++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs deleted file mode 100644 index c4d3d476..00000000 --- a/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -// using System.Diagnostics; -// using Lab.EnvFileConfig; -// using Microsoft.Extensions.Configuration; -// -// namespace App -// { -// public class Program -// { -// private static void Main() -// { -// var initialSettings = new Dictionary -// { -// ["Gender"] = "Male", -// ["Age"] = "18", -// ["ContactInfo:EmailAddress"] = "foobar@outlook.com", -// ["ContactInfo:PhoneNo"] = "123456789" -// }; -// -// var profile = new ConfigurationBuilder() -// .AddJsonFile("appSettings.json") -// .AddDatabase("DefaultDb", initialSettings) -// .Build() -// .Get(); -// -// Debug.Assert(profile.Gender == Gender.Male); -// Debug.Assert(profile.Age == 18); -// Debug.Assert(profile.ContactInfo.EmailAddress == "foobar@outlook.com"); -// Debug.Assert(profile.ContactInfo.PhoneNo == "123456789"); -// } -// } -// } - From da6e29d6a817243d9697008738e0261b0d5f3e8f Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 13 Feb 2022 20:01:04 +0800 Subject: [PATCH 152/267] add project --- Test/MultiTestCase/Makefile | 4 + Test/MultiTestCase/MultiTestCase.sln | 48 ++++++++++++ Test/MultiTestCase/docker-compose.yml | 10 +++ .../EmployeeAggregate/Entity/Employee.cs | 29 +++++++ .../EmployeeAggregate/IEmployeeAggregate.cs | 8 ++ .../Repository/IEmployeeRepository.cs | 16 ++++ .../src/Lab.Domain/Lab.Domain.csproj | 9 +++ .../AppDependencyInjectionExtensions.cs | 52 +++++++++++++ .../AppEnvironmentOption.cs | 31 ++++++++ .../EntityModel/Employee.cs | 30 ++++++++ .../EntityModel/EmployeeDbContext.cs | 75 +++++++++++++++++++ .../EntityModel/Identity.cs | 33 ++++++++ .../EntityModel/OrderHistory.cs | 30 ++++++++ .../EnvironmentAssistant.cs | 15 ++++ .../Lab.Infrastructure.DB.csproj | 15 ++++ .../Lab.MultiTestCase.UnitTest.csproj | 28 +++++++ .../Lab.MultiTestCase.UnitTest/MsTestHook.cs | 31 ++++++++ .../TestInstanceManager.cs | 34 +++++++++ .../Lab.MultiTestCase.UnitTest/UnitTest1.cs | 24 ++++++ .../Lab.MultiTestCase.csproj | 14 ++++ 20 files changed, 536 insertions(+) create mode 100644 Test/MultiTestCase/Makefile create mode 100644 Test/MultiTestCase/MultiTestCase.sln create mode 100644 Test/MultiTestCase/docker-compose.yml create mode 100644 Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Entity/Employee.cs create mode 100644 Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/IEmployeeAggregate.cs create mode 100644 Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs create mode 100644 Test/MultiTestCase/src/Lab.Domain/Lab.Domain.csproj create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/OrderHistory.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Test/MultiTestCase/src/Lab.Infrastructure.DB/Lab.Infrastructure.DB.csproj create mode 100644 Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/Lab.MultiTestCase.UnitTest.csproj create mode 100644 Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/MsTestHook.cs create mode 100644 Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/TestInstanceManager.cs create mode 100644 Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/UnitTest1.cs create mode 100644 Test/MultiTestCase/src/Lab.MultiTestCase/Lab.MultiTestCase.csproj diff --git a/Test/MultiTestCase/Makefile b/Test/MultiTestCase/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Test/MultiTestCase/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Test/MultiTestCase/MultiTestCase.sln b/Test/MultiTestCase/MultiTestCase.sln new file mode 100644 index 00000000..fe689a03 --- /dev/null +++ b/Test/MultiTestCase/MultiTestCase.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.MultiTestCase", "src\Lab.MultiTestCase\Lab.MultiTestCase.csproj", "{8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.MultiTestCase.UnitTest", "src\Lab.MultiTestCase.UnitTest\Lab.MultiTestCase.UnitTest.csproj", "{8F1C53C4-CE7C-447A-B627-3D428DA818A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Domain", "src\Lab.Domain\Lab.Domain.csproj", "{A3EFE099-E9FB-41F4-9718-087AFA4E0A7B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Infrastructure.DB", "src\Lab.Infrastructure.DB\Lab.Infrastructure.DB.csproj", "{CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57}.Release|Any CPU.Build.0 = Release|Any CPU + {8F1C53C4-CE7C-447A-B627-3D428DA818A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F1C53C4-CE7C-447A-B627-3D428DA818A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F1C53C4-CE7C-447A-B627-3D428DA818A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F1C53C4-CE7C-447A-B627-3D428DA818A2}.Release|Any CPU.Build.0 = Release|Any CPU + {A3EFE099-E9FB-41F4-9718-087AFA4E0A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3EFE099-E9FB-41F4-9718-087AFA4E0A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3EFE099-E9FB-41F4-9718-087AFA4E0A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3EFE099-E9FB-41F4-9718-087AFA4E0A7B}.Release|Any CPU.Build.0 = Release|Any CPU + {CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8F6CEAB7-4486-4AD6-8AAE-5DBEDA449E57} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {8F1C53C4-CE7C-447A-B627-3D428DA818A2} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A3EFE099-E9FB-41F4-9718-087AFA4E0A7B} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {CD1E06DF-79FC-4189-88D8-05F4E5C8DC7B} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Test/MultiTestCase/docker-compose.yml b/Test/MultiTestCase/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Test/MultiTestCase/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Entity/Employee.cs b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Entity/Employee.cs new file mode 100644 index 00000000..85d92ad1 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Entity/Employee.cs @@ -0,0 +1,29 @@ +namespace Lab.Domain.Entity; + +public record Employee +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public int? Age { get; set; } + + public string Remark { get; set; } + + public DateTimeOffset CreateAt { get; set; } + + public string CreateBy { get; set; } + + public Employee SetName(string name) + { + this.Name = name; + return this; + } + + public Employee SetAge(int age) + { + this.Age = age; + return this; + } + +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..4d72246e --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -0,0 +1,8 @@ +using Lab.Domain.Entity; + +namespace Lab.Domain; + +public interface IEmployeeAggregate +{ + Employee InsertAsync(Employee employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..898a9e1d --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -0,0 +1,16 @@ +using Lab.Domain.Entity; + +namespace Lab.Domain.Repository; + +public interface IEmployeeRepository +{ + Task InsertAsync(Employee employee, CancellationToken cancel = default); +} + +class EmployeeRepository : IEmployeeRepository +{ + public Task InsertAsync(Employee employee, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } +} diff --git a/Test/MultiTestCase/src/Lab.Domain/Lab.Domain.csproj b/Test/MultiTestCase/src/Lab.Domain/Lab.Domain.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Domain/Lab.Domain.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..c0960243 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,52 @@ +using Lab.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => + { + builder.AddConsole(); + }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + ; + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppEnvironmentOption.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..eb657155 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,31 @@ +namespace Lab.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _employeeDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Employee.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..f90ea3ad --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.Infrastructure.DB.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..e657ab04 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,75 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.Infrastructure.DB.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + + if (memoryOptions == null) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } + } + + s_migrated[0] = true; + } + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Identity.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..fe8b8e3b --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.Infrastructure.DB.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/OrderHistory.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..0eb33d75 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.Infrastructure.DB.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/EnvironmentAssistant.cs b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..72a3c6ee --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.Infrastructure.DB/Lab.Infrastructure.DB.csproj b/Test/MultiTestCase/src/Lab.Infrastructure.DB/Lab.Infrastructure.DB.csproj new file mode 100644 index 00000000..9fcdb0d8 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.Infrastructure.DB/Lab.Infrastructure.DB.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/Lab.MultiTestCase.UnitTest.csproj b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/Lab.MultiTestCase.UnitTest.csproj new file mode 100644 index 00000000..ecb158eb --- /dev/null +++ b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/Lab.MultiTestCase.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/MsTestHook.cs b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..c48c99fe --- /dev/null +++ b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// +// namespace Lab.MultiTestCase.UnitTest; +// +// [TestClass] +// public class MsTestHook +// { +// [AssemblyCleanup] +// public static void Cleanup() +// { +// TestInstanceManager.SetTestEnvironmentVariable(); +// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); +// if (db.Database.CanConnect()) +// { +// db.Database.EnsureDeleted(); +// } +// } +// +// [AssemblyInitialize] +// public static void Setup(TestContext context) +// { +// TestInstanceManager.SetTestEnvironmentVariable(); +// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); +// if (db.Database.CanConnect()) +// { +// db.Database.EnsureDeleted(); +// } +// +// db.Database.EnsureCreated(); +// } +// } \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/TestInstanceManager.cs b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/TestInstanceManager.cs new file mode 100644 index 00000000..16ca6112 --- /dev/null +++ b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/TestInstanceManager.cs @@ -0,0 +1,34 @@ +// using System; +// using Lab.MultiTestCase.EntityModel; +// using Microsoft.EntityFrameworkCore; +// using Microsoft.Extensions.DependencyInjection; +// +// namespace Lab.MultiTestCase.UnitTest; +// +// internal class TestInstanceManager +// { +// private static IServiceProvider _serviceProvider; +// +// public static IDbContextFactory EmployeeDbContextFactory => +// _serviceProvider.GetService>(); +// +// static TestInstanceManager() +// { +// var services = new ServiceCollection(); +// ConfigureTestServices(services); +// } +// +// public static void ConfigureTestServices(IServiceCollection services) +// { +// services.AddAppEnvironment(); +// services.AddEntityFramework(); +// _serviceProvider = services.BuildServiceProvider(); +// } +// +// public static void SetTestEnvironmentVariable() +// { +// var option = _serviceProvider.GetService(); +// option.EmployeeDbConnectionString = +// "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; +// } +// } \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/UnitTest1.cs b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/UnitTest1.cs new file mode 100644 index 00000000..79d1a00c --- /dev/null +++ b/Test/MultiTestCase/src/Lab.MultiTestCase.UnitTest/UnitTest1.cs @@ -0,0 +1,24 @@ +using System; +using Lab.Domain.Entity; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MultiTestCase.UnitTest; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void AddRanges() + { + var source = new Employee() + { + Id = Guid.NewGuid(), + Age = 18, + Name = "yao" + }; + + source.Age = 20; + Assert.AreEqual(18,source.Age); + } + +} \ No newline at end of file diff --git a/Test/MultiTestCase/src/Lab.MultiTestCase/Lab.MultiTestCase.csproj b/Test/MultiTestCase/src/Lab.MultiTestCase/Lab.MultiTestCase.csproj new file mode 100644 index 00000000..4ed8d60e --- /dev/null +++ b/Test/MultiTestCase/src/Lab.MultiTestCase/Lab.MultiTestCase.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + From 0b0745fc8789f6a426749367312f339376d16490 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 13 Feb 2022 20:14:21 +0800 Subject: [PATCH 153/267] feat: add project --- .../ChangeTracking/ChangeTracking.sln | 41 ++++++++++ .../ChangeTracking/Makefile | 4 + .../ChangeTracking/docker-compose.yml | 10 +++ .../Lab.ChangeTracking.Domain.UnitTest.csproj | 27 +++++++ .../MsTestHook.cs | 31 ++++++++ .../TestInstanceManager.cs | 34 +++++++++ .../UnitTest1.cs | 24 ++++++ .../EmployeeAggregate/Entity/Employee.cs | 29 +++++++ .../EmployeeAggregate/IEmployeeAggregate.cs | 8 ++ .../Repository/IEmployeeRepository.cs | 16 ++++ .../Lab.ChangeTracking.Domain.csproj | 9 +++ .../AppDependencyInjectionExtensions.cs | 52 +++++++++++++ .../AppEnvironmentOption.cs | 31 ++++++++ .../EntityModel/Employee.cs | 30 ++++++++ .../EntityModel/EmployeeDbContext.cs | 75 +++++++++++++++++++ .../EntityModel/Identity.cs | 33 ++++++++ .../EntityModel/OrderHistory.cs | 30 ++++++++ .../EnvironmentAssistant.cs | 15 ++++ ...ab.ChangeTracking.Infrastructure.DB.csproj | 15 ++++ 19 files changed, 514 insertions(+) create mode 100644 Property Change Tracking/ChangeTracking/ChangeTracking.sln create mode 100644 Property Change Tracking/ChangeTracking/Makefile create mode 100644 Property Change Tracking/ChangeTracking/docker-compose.yml create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj diff --git a/Property Change Tracking/ChangeTracking/ChangeTracking.sln b/Property Change Tracking/ChangeTracking/ChangeTracking.sln new file mode 100644 index 00000000..1c868fe3 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/ChangeTracking.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Infrastructure.DB", "src\Lab.ChangeTracking.Infrastructure.DB\Lab.ChangeTracking.Infrastructure.DB.csproj", "{A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain", "src\Lab.ChangeTracking.Domain\Lab.ChangeTracking.Domain.csproj", "{A1C827F2-683E-470C-A3DE-BD6DD2BE5198}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain.UnitTest", "src\Lab.ChangeTracking.Domain.UnitTest\Lab.ChangeTracking.Domain.UnitTest.csproj", "{7066EE2C-28A8-4408-B212-E582608AB967}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.Build.0 = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.Build.0 = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {7066EE2C-28A8-4408-B212-E582608AB967} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Property Change Tracking/ChangeTracking/Makefile b/Property Change Tracking/ChangeTracking/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Property Change Tracking/ChangeTracking/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/docker-compose.yml b/Property Change Tracking/ChangeTracking/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj new file mode 100644 index 00000000..1fb69aa7 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..c48c99fe --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// +// namespace Lab.MultiTestCase.UnitTest; +// +// [TestClass] +// public class MsTestHook +// { +// [AssemblyCleanup] +// public static void Cleanup() +// { +// TestInstanceManager.SetTestEnvironmentVariable(); +// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); +// if (db.Database.CanConnect()) +// { +// db.Database.EnsureDeleted(); +// } +// } +// +// [AssemblyInitialize] +// public static void Setup(TestContext context) +// { +// TestInstanceManager.SetTestEnvironmentVariable(); +// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); +// if (db.Database.CanConnect()) +// { +// db.Database.EnsureDeleted(); +// } +// +// db.Database.EnsureCreated(); +// } +// } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs new file mode 100644 index 00000000..16ca6112 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs @@ -0,0 +1,34 @@ +// using System; +// using Lab.MultiTestCase.EntityModel; +// using Microsoft.EntityFrameworkCore; +// using Microsoft.Extensions.DependencyInjection; +// +// namespace Lab.MultiTestCase.UnitTest; +// +// internal class TestInstanceManager +// { +// private static IServiceProvider _serviceProvider; +// +// public static IDbContextFactory EmployeeDbContextFactory => +// _serviceProvider.GetService>(); +// +// static TestInstanceManager() +// { +// var services = new ServiceCollection(); +// ConfigureTestServices(services); +// } +// +// public static void ConfigureTestServices(IServiceCollection services) +// { +// services.AddAppEnvironment(); +// services.AddEntityFramework(); +// _serviceProvider = services.BuildServiceProvider(); +// } +// +// public static void SetTestEnvironmentVariable() +// { +// var option = _serviceProvider.GetService(); +// option.EmployeeDbConnectionString = +// "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; +// } +// } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs new file mode 100644 index 00000000..2f0fe014 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs @@ -0,0 +1,24 @@ +using System; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void AddRanges() + { + var source = new Employee() + { + Id = Guid.NewGuid(), + Age = 18, + Name = "yao" + }; + + source.Age = 20; + Assert.AreEqual(18,source.Age); + } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs new file mode 100644 index 00000000..b1271581 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs @@ -0,0 +1,29 @@ +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +public record Employee +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public int? Age { get; set; } + + public string Remark { get; set; } + + public DateTimeOffset CreateAt { get; set; } + + public string CreateBy { get; set; } + + public Employee SetName(string name) + { + this.Name = name; + return this; + } + + public Employee SetAge(int age) + { + this.Age = age; + return this; + } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..04ae8d69 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -0,0 +1,8 @@ +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate; + +public interface IEmployeeAggregate +{ + Employee InsertAsync(Employee employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..7688ac6c --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -0,0 +1,16 @@ +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; + +public interface IEmployeeRepository +{ + Task InsertAsync(Employee employee, CancellationToken cancel = default); +} + +class EmployeeRepository : IEmployeeRepository +{ + public Task InsertAsync(Employee employee, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } +} diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..b5cdcb8b --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,52 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ChangeTracking.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => + { + builder.AddConsole(); + }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + ; + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..e29f53f3 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,31 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _employeeDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..6bc11f3c --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..1f651e6d --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,75 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + + if (memoryOptions == null) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + Console.WriteLine( + $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); + this.Database.Migrate(); + } + } + + s_migrated[0] = true; + } + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + }); + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..fc557c2b --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..914d8af6 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..b115d8a2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj new file mode 100644 index 00000000..9fcdb0d8 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + From 78fbb9e4c9bb4a3dbb9d719eb462b41c0edd5640 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 16 Feb 2022 23:33:47 +0800 Subject: [PATCH 154/267] =?UTF-8?q?Repo=20=E5=AF=A6=E4=BD=9C=20track=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 65 + .../Lab.ChangeTracking.Domain.UnitTest.csproj | 1 + .../TestAssistants.cs | 48 + .../TestInstanceManager.cs | 34 - .../UnitTest1.cs | 24 - .../Lab.ChangeTracking.Domain/Annotations.cs | 1603 +++++++++++++++++ .../Lab.ChangeTracking.Domain/BaseEntity.cs | 30 + .../EmployeeAggregate/EmployeeAggregate.cs | 24 + .../EmployeeAggregate/Entity/Employee.cs | 29 - .../Entity/EmployeeEntity.cs | 21 + .../Entity/EmployeeEntity2.cs | 49 + .../EmployeeAggregate/IEmployeeAggregate.cs | 2 +- .../Repository/EmployeeRepository.cs | 30 + .../Repository/IEmployeeRepository.cs | 12 +- .../Lab.ChangeTracking.Domain/IChangeable.cs | 14 + .../Lab.ChangeTracking.Domain.csproj | 9 + .../PropertyChangeTracker.cs | 41 + .../src/Lab.ChangeTracking.Domain/Survey.cs | 39 + 18 files changed, 1977 insertions(+), 98 deletions(-) create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs delete mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs delete mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Annotations.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/BaseEntity.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs delete mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity2.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/IChangeable.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Survey.cs diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs new file mode 100644 index 00000000..788d76a2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -0,0 +1,65 @@ +using System; +using System.ComponentModel; +using ChangeTracking; +using Lab.ChangeTracking.Domain.EmployeeAggregate; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Lab.MultiTestCase.UnitTest; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class ChangeTrackingUnitTest +{ + private IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + + public ChangeTrackingUnitTest() + { + } + + [TestMethod] + public void 原本用法() + { + var source = new EmployeeEntity() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + }; + source.Age = 18; + Assert.AreEqual(18, source.Age); + } + + [TestMethod] + public void 追蹤() + { + var source = new EmployeeEntity() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + }; + var tracked = source.AsTrackable(); + tracked.Age = 18; + + // source.Age = 18; + var trackable = tracked.CastToIChangeTrackable(); + + Assert.AreEqual(18, source.Age); + } + + [TestMethod] + public void Repository_追蹤() + { + var source = new EmployeeEntity() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + }; + var employeeEntity = this._employeeAggregate.ModifyAsync(source).Result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj index 1fb69aa7..72a505c5 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -8,6 +8,7 @@ + diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs new file mode 100644 index 00000000..68fe6717 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -0,0 +1,48 @@ +using System; +using Lab.ChangeTracking.Domain.EmployeeAggregate; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; +using Lab.ChangeTracking.Infrastructure.DB; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.MultiTestCase.UnitTest; + +// assistant +internal class TestAssistants +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + public static IEmployeeRepository EmployeeRepository => + _serviceProvider.GetService(); + + public static IEmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService(); + + static TestAssistants() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + SetTestEnvironmentVariable(); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + + services.AddSingleton(); + services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs deleted file mode 100644 index 16ca6112..00000000 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/TestInstanceManager.cs +++ /dev/null @@ -1,34 +0,0 @@ -// using System; -// using Lab.MultiTestCase.EntityModel; -// using Microsoft.EntityFrameworkCore; -// using Microsoft.Extensions.DependencyInjection; -// -// namespace Lab.MultiTestCase.UnitTest; -// -// internal class TestInstanceManager -// { -// private static IServiceProvider _serviceProvider; -// -// public static IDbContextFactory EmployeeDbContextFactory => -// _serviceProvider.GetService>(); -// -// static TestInstanceManager() -// { -// var services = new ServiceCollection(); -// ConfigureTestServices(services); -// } -// -// public static void ConfigureTestServices(IServiceCollection services) -// { -// services.AddAppEnvironment(); -// services.AddEntityFramework(); -// _serviceProvider = services.BuildServiceProvider(); -// } -// -// public static void SetTestEnvironmentVariable() -// { -// var option = _serviceProvider.GetService(); -// option.EmployeeDbConnectionString = -// "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; -// } -// } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs deleted file mode 100644 index 2f0fe014..00000000 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/UnitTest1.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Lab.ChangeTracking.Domain.UnitTest; - -[TestClass] -public class UnitTest1 -{ - [TestMethod] - public void AddRanges() - { - var source = new Employee() - { - Id = Guid.NewGuid(), - Age = 18, - Name = "yao" - }; - - source.Age = 20; - Assert.AreEqual(18,source.Age); - } - -} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Annotations.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Annotations.cs new file mode 100644 index 00000000..c93bbecf --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Annotations.cs @@ -0,0 +1,1603 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; +// ReSharper disable UnusedType.Global + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace Lab.ChangeTracking.Domain.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so checking for null is required before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element can never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + /// + /// public void Foo([ItemNotNull]List<string> books) + /// { + /// foreach (var book in books) { + /// if (book != null) // Warning: Expression is always true + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + /// + /// public void Foo([ItemCanBeNull]List<string> books) + /// { + /// foreach (var book in books) + /// { + /// // Warning: Possible 'System.NullReferenceException' + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by the format pattern and (optional) arguments. + /// The parameter, which contains the format string, should be given in the constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as the format string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; } + } + + /// + /// Indicates that the marked parameter is a message template where placeholders are to be replaced by the following arguments + /// in the order in which they appear + /// + /// + /// void LogInfo([StructuredMessageTemplate]string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// LogInfo("User created: {username}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class StructuredMessageTemplateAttribute : Attribute {} + + /// + /// Use this annotation to specify a type that contains static or const fields + /// with values for the annotated property/field/parameter. + /// The specified type will be used to improve completion suggestions. + /// + /// + /// namespace TestNamespace + /// { + /// public class Constants + /// { + /// public static int INT_CONST = 1; + /// public const string STRING_CONST = "1"; + /// } + /// + /// public class Class1 + /// { + /// [ValueProvider("TestNamespace.Constants")] public int myField; + /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } + /// + /// public void Test() + /// { + /// Foo(/*try completion here*/);// + /// myField = /*try completion here*/ + /// } + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Indicates that the integral value falls into the specified interval. + /// It's allowed to specify multiple non-intersecting intervals. + /// Values of interval boundaries are inclusive. + /// + /// + /// void Foo([ValueRange(0, 100)] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate, + AllowMultiple = true)] + public sealed class ValueRangeAttribute : Attribute + { + public object From { get; } + public object To { get; } + + public ValueRangeAttribute(long from, long to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(ulong from, ulong to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(long value) + { + From = To = value; + } + + public ValueRangeAttribute(ulong value) + { + From = To = value; + } + } + + /// + /// Indicates that the integral value never falls below zero. + /// + /// + /// void Foo([NonNegativeValue] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate)] + public sealed class NonNegativeValueAttribute : Attribute { } + + /// + /// Indicates that the function argument should be a string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If the method has a single input parameter, its name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for the method output + /// means that the method doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by the semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by the analysis engine.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("null <= param:null")] // reverse condition syntax + /// public string GetName(string surname) + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; } + + public bool ForceFullStates { get; } + } + + /// + /// Indicates whether the marked element should be localized. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will be ignored by usage-checking inspections.
+ /// You can use and + /// to configure how this attribute is applied. + ///
+ /// + /// [UsedImplicitly] + /// public class TypeConverter {} + /// + /// public class SummaryData + /// { + /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] + /// public SummaryData() {} + /// } + /// + /// [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.Default)] + /// public interface IService {} + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; } + + public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Can be applied to attributes, type parameters, and parameters of a type assignable from . + /// When applied to an attribute, the decorated attribute behaves the same as . + /// When applied to a type parameter or to a parameter of type , + /// indicates that the corresponding type is used implicitly. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Specifies the details of implicitly used symbol when it is marked + /// with or . + /// + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specifies what is considered to be used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of the type marked with the attribute are considered used. + Members = 2, + /// Inherited entities are considered used. + WithInheritors = 4, + /// Entity marked with the attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API, + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [AttributeUsage(AttributeTargets.All, Inherited = false)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; } + } + + /// + /// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate can only be invoked during method execution + /// (the delegate can be invoked zero or multiple times, but not stored to some field and invoked later, + /// when the containing method is no longer on the execution stack). + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// If is true, the attribute will only takes effect if the method invocation is located under the 'await' expression. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute + { + /// + /// Require the method invocation to be used under the 'await' expression for this attribute to take effect on code analysis engine. + /// Can be used for delegate/enumerable parameters of 'async' methods. + /// + public bool RequireAwait { get; set; } + } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Warning: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of the method invocation must be used. + /// + /// + /// Methods decorated with this attribute (in contrast to pure methods) might change state, + /// but make no sense without using their return value.
+ /// Similarly to , this attribute + /// will help to detect usages of the method when the return value is not used. + /// Optionally, you can specify a message to use when showing warnings, e.g. + /// [MustUseReturnValue("Use the return value to...")]. + ///
+ [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; } + } + + /// + /// This annotation allows to enforce allocation-less usage patterns of delegates for performance-critical APIs. + /// When this annotation is applied to the parameter of delegate type, IDE checks the input argument of this parameter: + /// * When lambda expression or anonymous method is passed as an argument, IDE verifies that the passed closure + /// has no captures of the containing local variables and the compiler is able to cache the delegate instance + /// to avoid heap allocations. Otherwise the warning is produced. + /// * IDE warns when method name or local function name is passed as an argument as this always results + /// in heap allocation of the delegate instance. + /// + /// + /// In C# 9.0 code IDE would also suggest to annotate the anonymous function with 'static' modifier + /// to make use of the similar analysis provided by the language/compiler. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public class RequireStaticDelegateAttribute : Attribute + { + public bool IsError { get; set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value of that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; } + } + + /// + /// An extension method marked with this attribute is processed by code completion + /// as a 'Source Template'. When the extension method is completed over some expression, its source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Razor attribute. Indicates that the marked parameter or method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation, or property access + /// over collection type affects the contents of the collection. + /// When applied to a return value of a method indicates if the returned collection + /// is created exclusively for the caller (CollectionAccessType.UpdatedContent) or + /// can be read/updated from outside (CollectionAccessType.Read | CollectionAccessType.UpdatedContent) + /// Use to specify the access type. + /// + /// + /// Using this attribute only makes sense if all collection methods are marked with this attribute. + /// + /// + /// public class MyStringCollection : List<string> + /// { + /// [CollectionAccess(CollectionAccessType.Read)] + /// public string GetFirstString() + /// { + /// return this.ElementAt(0); + /// } + /// } + /// class Test + /// { + /// public void Foo() + /// { + /// // Warning: Contents of the collection is never updated + /// var col = new MyStringCollection(); + /// string x = col.GetFirstString(); + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.ReturnValue)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; } + } + + /// + /// Provides a value for the to define + /// how the collection method invocation affects the contents of the collection. + /// + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts the control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that the method is a pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable passed as a parameter is not enumerated. + /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. + /// + /// + /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class + /// { + /// // custom check for null but no enumeration + /// } + /// + /// void Foo(IEnumerable<string> values) + /// { + /// ThrowIfNull(values, nameof(values)); + /// var x = values.ToList(); // No warnings about multiple enumeration + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that the marked parameter, field, or property is a regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Language of injected code fragment inside marked by string literal. + /// + public enum InjectedLanguage + { + CSS, + HTML, + JAVASCRIPT, + JSON, + XML + } + + /// + /// Indicates that the marked parameter, field, or property is accepting a string literal + /// containing code fragment in a language specified by the . + /// + /// + /// void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps) + /// { + /// // cssProps should only contains a list of CSS properties + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class LanguageInjectionAttribute : Attribute + { + public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) + { + InjectedLanguage = injectedLanguage; + } + + /// Specify a language of injected code fragment. + public InjectedLanguage InjectedLanguage { get; } + /// Specify a string that "precedes" injected string literal. + [CanBeNull] public string Prefix { get; set; } + /// Specify a string that "follows" injected string literal. + [CanBeNull] public string Suffix { get; set; } + } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some Style-derived type, that + /// is used to style items of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates that DependencyProperty has OneWay binding mode by default. + /// + /// + /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } + + /// + /// XAML attribute. Indicates that DependencyProperty has TwoWay binding mode by default. + /// + /// + /// This attribute must be applied to DependencyProperty's CLR accessor property if it is present, to DependencyProperty descriptor field otherwise. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; } + + [NotNull] public Type ControlType { get; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; } + + [NotNull] public string FieldName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; } + [CanBeNull] public string PageName { get; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } + + /// + /// Indicates that the marked parameter, field, or property is a route template. + /// + /// + /// This attribute allows IDE to recognize the use of web frameworks' route templates + /// to enable syntax highlighting, code completion, navigation, rename and other features in string literals. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class RouteTemplateAttribute : Attribute { } + + /// + /// Indicates that the marked type is custom route parameter constraint, + /// which is registered in application's Startup with name ConstraintName + /// + /// + /// You can specify ProposedType if target constraint matches only route parameters of specific type, + /// it will allow IDE to create method's parameter from usage in route template + /// with specified type instead of default System.String + /// and check if constraint's proposed type conflicts with matched parameter's type + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class RouteParameterConstraintAttribute : Attribute + { + [NotNull] public string ConstraintName { get; } + [CanBeNull] public Type ProposedType { get; set; } + + public RouteParameterConstraintAttribute([NotNull] string constraintName) + { + ConstraintName = constraintName; + } + } + + /// + /// Indicates that the marked parameter, field, or property is an URI string. + /// + /// + /// This attribute enables code completion, navigation, rename and other features + /// in URI string literals assigned to annotated parameter, field or property. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class UriStringAttribute : Attribute + { + public UriStringAttribute() { } + + public UriStringAttribute(string httpVerb) + { + HttpVerb = httpVerb; + } + + [CanBeNull] public string HttpVerb { get; } + } + + /// + /// + /// Defines the code search template using the Structural Search and Replace syntax. + /// It allows you to find and, if necessary, replace blocks of code that match a specific pattern. + /// Search and replace patterns consist of a textual part and placeholders. + /// Textural part must contain only identifiers allowed in the target language and will be matched exactly (white spaces, tabulation characters, and line breaks are ignored). + /// Placeholders allow matching variable parts of the target code blocks. + /// A placeholder has the following format: $placeholder_name$- where placeholder_name is an arbitrary identifier. + /// + /// + /// Available placeholders: + /// + /// $this$ - expression of containing type + /// $thisType$ - containing type + /// $member$ - current member placeholder + /// $qualifier$ - this placeholder is available in the replace pattern and can be used to insert qualifier expression matched by the $member$ placeholder. + /// (Note that if $qualifier$ placeholder is used, then $member$ placeholder will match only qualified references) + /// $expression$ - expression of any type + /// $identifier$ - identifier placeholder + /// $args$ - any number of arguments + /// $arg$ - single argument + /// $arg1$ ... $arg10$ - single argument + /// $stmts$ - any number of statements + /// $stmt$ - single statement + /// $stmt1$ ... $stmt10$ - single statement + /// $name{Expression, 'Namespace.FooType'}$ - expression with 'Namespace.FooType' type + /// $expression{'Namespace.FooType'}$ - expression with 'Namespace.FooType' type + /// $name{Type, 'Namespace.FooType'}$ - 'Namespace.FooType' type + /// $type{'Namespace.FooType'}$ - 'Namespace.FooType' type + /// $statement{1,2}$ - 1 or 2 statements + /// + /// + /// + /// Note that you can also define your own placeholders of the supported types and specify arguments for each placeholder type. + /// This can be done using the following format: $name{type, arguments}$. Where 'name' - is the name of your placeholder, + /// 'type' - is the type of your placeholder (one of the following: Expression, Type, Identifier, Statement, Argument, Member), + /// 'arguments' - arguments list for your placeholder. Each placeholder type supports it's own arguments, check examples below for mode details. + /// Placeholder type may be omitted and determined from the placeholder name, if name has one of the following prefixes: + /// + /// expr, expression - expression placeholder, e.g. $exprPlaceholder{}$, $expressionFoo{}$ + /// arg, argument - argument placeholder, e.g. $argPlaceholder{}$, $argumentFoo{}$ + /// ident, identifier - identifier placeholder, e.g. $identPlaceholder{}$, $identifierFoo{}$ + /// stmt, statement - statement placeholder, e.g. $stmtPlaceholder{}$, $statementFoo{}$ + /// type - type placeholder, e.g. $typePlaceholder{}$, $typeFoo{}$ + /// member - member placeholder, e.g. $memberPlaceholder{}$, $memberFoo{}$ + /// + /// + /// + /// Expression placeholder arguments: + /// + /// expressionType - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myExpr{Expression, 'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. + /// $myExpr{Expression, 'Namespace.FooType'}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type or expressions which can be implicitly converted to 'Namespace.FooType'. + /// $myExpr{Expression}$ - defines expression placeholder, matching expressions of any type. + /// $exprFoo{'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. + /// + /// + /// + /// Type placeholder arguments: + /// + /// type - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myType{Type, 'Namespace.FooType', true}$ - defines type placeholder, matching 'Namespace.FooType' types with exact matching. + /// $myType{Type, 'Namespace.FooType'}$ - defines type placeholder, matching 'Namespace.FooType' types or types, which can be implicitly converted to 'Namespace.FooType'. + /// $myType{Type}$ - defines type placeholder, matching any type. + /// $typeFoo{'Namespace.FooType', true}$ - defines types placeholder, matching 'Namespace.FooType' types with exact matching. + /// + /// + /// + /// Identifier placeholder arguments: + /// + /// nameRegex - string value in single quotes, specifies regex to use for matching (empty string by default) + /// nameRegexCaseSensitive - boolean value, specifies if name regex is case sensitive (true by default) + /// type - string value in single quotes, specifies full type name to match (empty string by default) + /// exactType - boolean value, specifies if expression should have exact type match (false by default) + /// + /// Examples: + /// + /// $myIdentifier{Identifier, 'my.*', false, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (ignoring case) starting with 'my' prefix with 'Namespace.FooType' type. + /// $myIdentifier{Identifier, 'my.*', true, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix with 'Namespace.FooType' type. + /// $identFoo{'my.*'}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix. + /// + /// + /// + /// Statement placeholder arguments: + /// + /// minimalOccurrences - minimal number of statements to match (-1 by default) + /// maximalOccurrences - maximal number of statements to match (-1 by default) + /// + /// Examples: + /// + /// $myStmt{Statement, 1, 2}$ - defines statement placeholder, matching 1 or 2 statements. + /// $myStmt{Statement}$ - defines statement placeholder, matching any number of statements. + /// $stmtFoo{1, 2}$ - defines statement placeholder, matching 1 or 2 statements. + /// + /// + /// + /// Argument placeholder arguments: + /// + /// minimalOccurrences - minimal number of arguments to match (-1 by default) + /// maximalOccurrences - maximal number of arguments to match (-1 by default) + /// + /// Examples: + /// + /// $myArg{Argument, 1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. + /// $myArg{Argument}$ - defines argument placeholder, matching any number of arguments. + /// $argFoo{1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. + /// + /// + /// + /// Member placeholder arguments: + /// + /// docId - string value in single quotes, specifies XML documentation id of the member to match (empty by default) + /// + /// Examples: + /// + /// $myMember{Member, 'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. + /// $memberFoo{'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. + /// + /// + /// + /// For more information please refer to the Structural Search and Replace article. + /// + /// + [AttributeUsage( + AttributeTargets.Method + | AttributeTargets.Constructor + | AttributeTargets.Property + | AttributeTargets.Field + | AttributeTargets.Event + | AttributeTargets.Interface + | AttributeTargets.Class + | AttributeTargets.Struct + | AttributeTargets.Enum, + AllowMultiple = true, + Inherited = false)] + public sealed class CodeTemplateAttribute : Attribute + { + public CodeTemplateAttribute(string searchTemplate) + { + SearchTemplate = searchTemplate; + } + + /// + /// Structural search pattern to use in the code template. + /// Pattern includes textual part, which must contain only identifiers allowed in the target language, + /// and placeholders, which allow matching variable parts of the target code blocks. + /// + public string SearchTemplate { get; } + + /// + /// Message to show when the search pattern was found. + /// You can also prepend the message text with "Error:", "Warning:", "Suggestion:" or "Hint:" prefix to specify the pattern severity. + /// Code patterns with replace template produce suggestions by default. + /// However, if replace template is not provided, then warning severity will be used. + /// + public string Message { get; set; } + + /// + /// Structural search replace pattern to use in code template replacement. + /// + public string ReplaceTemplate { get; set; } + + /// + /// Replace message to show in the light bulb. + /// + public string ReplaceMessage { get; set; } + + /// + /// Apply code formatting after code replacement. + /// + public bool FormatAfterReplace { get; set; } = true; + + /// + /// Whether similar code blocks should be matched. + /// + public bool MatchSimilarConstructs { get; set; } + + /// + /// Automatically insert namespace import directives or remove qualifiers that become redundant after the template is applied. + /// + public bool ShortenReferences { get; set; } + + /// + /// String to use as a suppression key. + /// By default the following suppression key is used 'CodeTemplate_SomeType_SomeMember', + /// where 'SomeType' and 'SomeMember' are names of the associated containing type and member to which this attribute is applied. + /// + public string SuppressionKey { get; set; } + } +} diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/BaseEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/BaseEntity.cs new file mode 100644 index 00000000..91929a85 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/BaseEntity.cs @@ -0,0 +1,30 @@ +using System.Reflection; + +namespace Lab.ChangeTracking.Domain.Annotations; + +public record BaseEntity : IChangeable +{ + private PropertyChangeTracker _tracker = new(); + + public void Initial() + { + this._tracker.Initial(); + } + + public bool HasChanged { get; private set; } + + public Dictionary GetChangedProperties() + { + return this._tracker.GetChangedProperties(); + } + + public Dictionary GetOriginalValues() + { + throw new NotImplementedException(); + } + + public void Track(string propertyName, object value) + { + this._tracker.Track(propertyName, value); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs new file mode 100644 index 00000000..29a86465 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -0,0 +1,24 @@ +using ChangeTracking; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate; + +public class EmployeeAggregate : IEmployeeAggregate +{ + private IEmployeeRepository _repository; + + public EmployeeAggregate(IEmployeeRepository repository) + { + this._repository = repository; + } + + public async Task ModifyAsync(EmployeeEntity employee, CancellationToken cancel = default) + { + var trackable = employee.AsTrackable(); + trackable.Age = 20; + trackable.Name = "小章"; + var changeCount = await this._repository.ChangeAsync(trackable, cancel); + return trackable; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs deleted file mode 100644 index b1271581..00000000 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/Employee.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; - -public record Employee -{ - public Guid Id { get; set; } - - public string Name { get; set; } - - public int? Age { get; set; } - - public string Remark { get; set; } - - public DateTimeOffset CreateAt { get; set; } - - public string CreateBy { get; set; } - - public Employee SetName(string name) - { - this.Name = name; - return this; - } - - public Employee SetAge(int age) - { - this.Age = age; - return this; - } - -} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs new file mode 100644 index 00000000..08b63ed2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using ChangeTracking; +using Lab.ChangeTracking.Domain.Annotations; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +public class EmployeeEntity +{ + public virtual Guid Id { get; init; } + + public virtual string Name { get; set; } + + public virtual int? Age { get; set; } + + public virtual string Remark { get; set; } + + public virtual DateTimeOffset CreateAt { get; set; } + + public virtual string CreateBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity2.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity2.cs new file mode 100644 index 00000000..7171d53c --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity2.cs @@ -0,0 +1,49 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using ChangeTracking; +using Lab.ChangeTracking.Domain.Annotations; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +public record EmployeeEntity2 : BaseEntity +{ + private string _name; + private int? _age; + + public virtual Guid Id { get; init; } + + public virtual string Name + { + get => this._name; + init => this._name = value; + } + + public virtual int? Age + { + get => this._age; + init => this._age = value; + } + + public virtual string Remark { get; set; } + + public virtual DateTimeOffset CreateAt { get; set; } + + public virtual string CreateBy { get; set; } + + public EmployeeEntity2 SetName(string name) + { + this._name = name; + return this; + } + + public EmployeeEntity2 SetAge(int age) + { + if (this._age != age) + { + this._age = age; + this.Track(nameof(this.Age), age); + } + + return this; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs index 04ae8d69..9ec26110 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -4,5 +4,5 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate; public interface IEmployeeAggregate { - Employee InsertAsync(Employee employee, CancellationToken cancel = default); + Task ModifyAsync(EmployeeEntity employee, CancellationToken cancel = default); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs new file mode 100644 index 00000000..88ef6875 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -0,0 +1,30 @@ +using ChangeTracking; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; + +public class EmployeeRepository : IEmployeeRepository +{ + private readonly IDbContextFactory _memberContextFactory; + + public EmployeeRepository(IDbContextFactory memberContextFactory) + { + this._memberContextFactory = memberContextFactory; + } + + public async Task ChangeAsync(EmployeeEntity srcEmployee, + CancellationToken cancel = default) + { + var tracked = srcEmployee.CastToIChangeTrackable(); + + await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); + foreach (var changedProperty in tracked.ChangedProperties) + { + + } + + return await dbContext.SaveChangesAsync(cancel); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs index 7688ac6c..9b49e6a4 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -4,13 +4,5 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; public interface IEmployeeRepository { - Task InsertAsync(Employee employee, CancellationToken cancel = default); -} - -class EmployeeRepository : IEmployeeRepository -{ - public Task InsertAsync(Employee employee, CancellationToken cancel = default) - { - throw new NotImplementedException(); - } -} + Task ChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/IChangeable.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/IChangeable.cs new file mode 100644 index 00000000..65415d74 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/IChangeable.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain.Annotations; + +public interface IChangeable +{ + Dictionary GetChangedProperties(); + + Dictionary GetOriginalValues(); + + void Track(string propertyName, object value); + + void Initial(); + + bool HasChanged { get; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj index eb2460e9..55d86c31 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -6,4 +6,13 @@ enable + + + + + + + + + diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs new file mode 100644 index 00000000..12577d8b --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs @@ -0,0 +1,41 @@ +using System.Reflection; + +namespace Lab.ChangeTracking.Domain.Annotations; + +public class PropertyChangeTracker +{ + private Dictionary _changedProperties = new(); + private Dictionary _originalValues = new(); + + public void Initial() + { + var properties = this.GetType().GetProperties(); + foreach (var property in properties) + { + this._originalValues.Add(property.Name, property.GetValue(this)); + } + } + + public Dictionary GetChangedProperties() + { + return this._changedProperties; + } + + public Dictionary GetOriginalValues() + { + throw new NotImplementedException(); + } + + public void Track(string propertyName, object value) + { + var changes = this._changedProperties; + if (changes.ContainsKey(propertyName) == false) + { + changes.Add(propertyName, value); + } + else + { + changes[propertyName] = value; + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Survey.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Survey.cs new file mode 100644 index 00000000..dfa0a859 --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Survey.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; +using System.Reflection; + +namespace Lab.ChangeTracking.Domain.Annotations; + +public class Base : IRevertibleChangeTracking +{ + protected readonly Dictionary ChangedProperties = new(); + protected readonly Dictionary OriginalValues = new(); + + public void Initialize() + { + var properties = this.GetType().GetProperties(); + + // Save the current value of the properties to our dictionary. + foreach (var property in properties) + { + this.OriginalValues.Add(property.Name, property.GetValue(this)); + } + } + + public bool IsChanged { get; private set; } + + public void RejectChanges() + { + foreach (var property in this.ChangedProperties) + { + this.GetType().GetRuntimeProperty(property.Key).SetValue(this, property.Value); + } + + this.AcceptChanges(); + } + + public void AcceptChanges() + { + this.ChangedProperties.Clear(); + this.IsChanged = false; + } +} \ No newline at end of file From 15aea806db76def14108a11d8157ef8823f4f6d2 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 17 Feb 2022 13:11:24 +0800 Subject: [PATCH 155/267] =?UTF-8?q?feat:=20=E8=BF=BD=E8=B9=A4=E7=95=B0?= =?UTF-8?q?=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 30 ++++++++- .../MsTestHook.cs | 62 +++++++++---------- .../EmployeeAggregate/EmployeeAggregate.cs | 2 + .../Repository/EmployeeRepository.cs | 30 +++++++-- .../Lab.ChangeTracking.Domain.csproj | 1 + .../EntityModel/EmployeeDbContext.cs | 4 ++ 6 files changed, 90 insertions(+), 39 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 788d76a2..864181a6 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -52,14 +52,40 @@ public void 追蹤() } [TestMethod] - public void Repository_追蹤() + public void 追蹤後異動() { + var toDB = Insert(); var source = new EmployeeEntity() { - Id = Guid.NewGuid(), + Id = toDB.Id, Name = "yao", Age = 12, }; var employeeEntity = this._employeeAggregate.ModifyAsync(source).Result; } + + private static Employee Insert() + { + using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var toDB = new Employee() + { + Id = Guid.NewGuid(), + Age = 18, + Name = "yao", + CreateAt = DateTimeOffset.Now, + CreateBy = "TEST", + Identity = new Identity() + { + Account = "yao", + Password = "123456", + CreateAt = DateTimeOffset.Now, + CreateBy = "TEST", + } + }; + dbContext.Employees.Add(toDB); + dbContext.SaveChanges(); + return toDB; + } + + } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs index c48c99fe..ca75b47b 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -1,31 +1,31 @@ -// using Microsoft.VisualStudio.TestTools.UnitTesting; -// -// namespace Lab.MultiTestCase.UnitTest; -// -// [TestClass] -// public class MsTestHook -// { -// [AssemblyCleanup] -// public static void Cleanup() -// { -// TestInstanceManager.SetTestEnvironmentVariable(); -// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); -// if (db.Database.CanConnect()) -// { -// db.Database.EnsureDeleted(); -// } -// } -// -// [AssemblyInitialize] -// public static void Setup(TestContext context) -// { -// TestInstanceManager.SetTestEnvironmentVariable(); -// var db = TestInstanceManager.EmployeeDbContextFactory.CreateDbContext(); -// if (db.Database.CanConnect()) -// { -// db.Database.EnsureDeleted(); -// } -// -// db.Database.EnsureCreated(); -// } -// } \ No newline at end of file +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MultiTestCase.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index 29a86465..731d16dc 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -21,4 +21,6 @@ public async Task ModifyAsync(EmployeeEntity employee, Cancellat var changeCount = await this._repository.ChangeAsync(trackable, cancel); return trackable; } + + } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 88ef6875..4f21f5ac 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -1,4 +1,6 @@ -using ChangeTracking; +using System.Linq; +using ChangeTracking; +using EFCore.BulkExtensions; using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Microsoft.EntityFrameworkCore; @@ -20,11 +22,27 @@ public async Task ChangeAsync(EmployeeEntity srcEmployee, var tracked = srcEmployee.CastToIChangeTrackable(); await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); - foreach (var changedProperty in tracked.ChangedProperties) - { - - } + var employee = + await dbContext.Employees.FirstOrDefaultAsync(a => a.Id == srcEmployee.Id, cancellationToken: cancel); + var destEmployee = this.To(srcEmployee); + var updateColumns = tracked.ChangedProperties.ToList(); + var changeCount = await dbContext.Employees + .Where(a => a.Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee, updateColumns, cancel); + + return changeCount; + } - return await dbContext.SaveChangesAsync(cancel); + public Employee To(EmployeeEntity srcEmployee) + { + return new Employee + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Remark = srcEmployee.Remark, + CreateAt = srcEmployee.CreateAt, + CreateBy = srcEmployee.CreateBy, + }; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj index 55d86c31..31373e41 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index 1f651e6d..db4d2c80 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -69,6 +69,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) p.HasIndex(e => e.SequenceId) .IsUnique() .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; }); } } From e54e01e62f336f8b2792aab06beeabc3a91696c6 Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 18 Feb 2022 01:58:34 +0800 Subject: [PATCH 156/267] =?UTF-8?q?=E7=95=B0=E5=8B=95=E8=A4=87=E9=9B=9C?= =?UTF-8?q?=E5=9E=8B=E5=88=A5-=E5=A4=B1=E6=95=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 3 ++- .../EmployeeAggregate/EmployeeAggregate.cs | 7 +++-- .../Entity/EmployeeEntity.cs | 2 ++ .../Entity/IdentityEntity.cs | 14 ++++++++++ .../EmployeeAggregate/IEmployeeAggregate.cs | 2 +- .../Repository/EmployeeRepository.cs | 27 ++++++++++++++++++- 6 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 864181a6..b1682639 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -60,8 +60,9 @@ public void 追蹤後異動() Id = toDB.Id, Name = "yao", Age = 12, + Identity = new IdentityEntity(){} }; - var employeeEntity = this._employeeAggregate.ModifyAsync(source).Result; + var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; } private static Employee Insert() diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index 731d16dc..57d7c8d9 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -13,14 +13,13 @@ public EmployeeAggregate(IEmployeeRepository repository) this._repository = repository; } - public async Task ModifyAsync(EmployeeEntity employee, CancellationToken cancel = default) + public async Task ModifyFlowAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) { - var trackable = employee.AsTrackable(); + var trackable = srcEmployee.AsTrackable(); trackable.Age = 20; trackable.Name = "小章"; + trackable.Identity.Password = "9527"; var changeCount = await this._repository.ChangeAsync(trackable, cancel); return trackable; } - - } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index 08b63ed2..eb669e1c 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -18,4 +18,6 @@ public class EmployeeEntity public virtual DateTimeOffset CreateAt { get; set; } public virtual string CreateBy { get; set; } + + public virtual IdentityEntity Identity { get; set; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs new file mode 100644 index 00000000..48e24fed --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +public class IdentityEntity +{ + public virtual string Account { get; set; } + + public virtual string Password { get; set; } + + public virtual string Remark { get; set; } + + public virtual DateTimeOffset CreateAt { get; set; } + + public virtual string CreateBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs index 9ec26110..8af395df 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -4,5 +4,5 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate; public interface IEmployeeAggregate { - Task ModifyAsync(EmployeeEntity employee, CancellationToken cancel = default); + Task ModifyFlowAsync(EmployeeEntity employee, CancellationToken cancel = default); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 4f21f5ac..42d35245 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -23,12 +23,26 @@ public async Task ChangeAsync(EmployeeEntity srcEmployee, await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); var employee = - await dbContext.Employees.FirstOrDefaultAsync(a => a.Id == srcEmployee.Id, cancellationToken: cancel); + await dbContext.Employees + .FirstOrDefaultAsync(a => a.Id == srcEmployee.Id, cancel); + var identity = + await dbContext.Identities + .FirstOrDefaultAsync(a => a.Employee_Id == srcEmployee.Id, cancel); + var destEmployee = this.To(srcEmployee); var updateColumns = tracked.ChangedProperties.ToList(); + var updateColumns1 = new List() + { + "Age", + nameof(srcEmployee.Identity) + }; var changeCount = await dbContext.Employees .Where(a => a.Id == srcEmployee.Id) .BatchUpdateAsync(destEmployee, updateColumns, cancel); + var changeCount1 = await dbContext.Identities + .Where(a => a.Employee_Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee.Identity, updateColumns1, cancel); + return changeCount; } @@ -43,6 +57,17 @@ public Employee To(EmployeeEntity srcEmployee) Remark = srcEmployee.Remark, CreateAt = srcEmployee.CreateAt, CreateBy = srcEmployee.CreateBy, + Identity = To(srcEmployee.Identity) + }; + } + public Identity To(IdentityEntity srcIdentity) + { + return new Identity() + { + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreateAt = srcIdentity.CreateAt, + CreateBy = srcIdentity.CreateBy, }; } } \ No newline at end of file From fc622712a150a36085e0bbb44d5ad41716eae1b7 Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 18 Feb 2022 18:09:33 +0800 Subject: [PATCH 157/267] add complex type --- .../ChangeTrackingUnitTest.cs | 5 +- .../EmployeeAggregate/EmployeeAggregate.cs | 7 +- .../Entity/EmployeeEntity.cs | 1 + .../Repository/EmployeeRepository.cs | 74 ++++++++++--------- .../Repository/IEmployeeRepository.cs | 2 +- 5 files changed, 50 insertions(+), 39 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index b1682639..cec066bb 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using ChangeTracking; using Lab.ChangeTracking.Domain.EmployeeAggregate; @@ -60,7 +61,9 @@ public void 追蹤後異動() Id = toDB.Id, Name = "yao", Age = 12, - Identity = new IdentityEntity(){} + Identity = new IdentityEntity(){}, + Profiles = new Dictionary() + }; var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; } diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index 57d7c8d9..ea2db4f4 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -16,10 +16,13 @@ public EmployeeAggregate(IEmployeeRepository repository) public async Task ModifyFlowAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) { var trackable = srcEmployee.AsTrackable(); - trackable.Age = 20; + trackable.Name = "小章"; trackable.Identity.Password = "9527"; - var changeCount = await this._repository.ChangeAsync(trackable, cancel); + // trackable.Profiles.Add("FirstName", "余"); + trackable.Profiles = new Dictionary() { { "Last", "小章" } }; + + var changeCount = await this._repository.SaveChangeAsync(trackable, cancel); return trackable; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index eb669e1c..651e77ee 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -15,6 +15,7 @@ public class EmployeeEntity public virtual string Remark { get; set; } + public virtual Dictionary Profiles { get; set; } public virtual DateTimeOffset CreateAt { get; set; } public virtual string CreateBy { get; set; } diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 42d35245..2f424ad5 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -1,5 +1,4 @@ -using System.Linq; -using ChangeTracking; +using ChangeTracking; using EFCore.BulkExtensions; using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; @@ -16,37 +15,6 @@ public EmployeeRepository(IDbContextFactory memberContextFact this._memberContextFactory = memberContextFactory; } - public async Task ChangeAsync(EmployeeEntity srcEmployee, - CancellationToken cancel = default) - { - var tracked = srcEmployee.CastToIChangeTrackable(); - - await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); - var employee = - await dbContext.Employees - .FirstOrDefaultAsync(a => a.Id == srcEmployee.Id, cancel); - var identity = - await dbContext.Identities - .FirstOrDefaultAsync(a => a.Employee_Id == srcEmployee.Id, cancel); - - var destEmployee = this.To(srcEmployee); - var updateColumns = tracked.ChangedProperties.ToList(); - var updateColumns1 = new List() - { - "Age", - nameof(srcEmployee.Identity) - }; - var changeCount = await dbContext.Employees - .Where(a => a.Id == srcEmployee.Id) - .BatchUpdateAsync(destEmployee, updateColumns, cancel); - var changeCount1 = await dbContext.Identities - .Where(a => a.Employee_Id == srcEmployee.Id) - .BatchUpdateAsync(destEmployee.Identity, updateColumns1, cancel); - - - return changeCount; - } - public Employee To(EmployeeEntity srcEmployee) { return new Employee @@ -57,12 +25,13 @@ public Employee To(EmployeeEntity srcEmployee) Remark = srcEmployee.Remark, CreateAt = srcEmployee.CreateAt, CreateBy = srcEmployee.CreateBy, - Identity = To(srcEmployee.Identity) + Identity = this.To(srcEmployee.Identity) }; } + public Identity To(IdentityEntity srcIdentity) { - return new Identity() + return new Identity { Password = srcIdentity.Password, Remark = srcIdentity.Remark, @@ -70,4 +39,39 @@ public Identity To(IdentityEntity srcIdentity) CreateBy = srcIdentity.CreateBy, }; } + + public async Task SaveChangeAsync(EmployeeEntity srcEmployee, + CancellationToken cancel = default) + { + var employeeTrackable = srcEmployee.CastToIChangeTrackable(); + var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); + + var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); + var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); + + await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); + await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); + + try + { + var destEmployee = this.To(srcEmployee); + var memberChangeCount = await dbContext.Employees + .Where(a => a.Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee, + memberChangeProperties, cancel); + var identityChangeCount = await dbContext.Identities + .Where(a => a.Employee_Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee.Identity, + identityChangeProperties, + cancel); + + await transaction.CommitAsync(cancel); + return memberChangeCount + identityChangeCount; + } + catch (Exception e) + { + await transaction.RollbackAsync(cancel); + throw new Exception("存檔失敗"); + } + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs index 9b49e6a4..3fd851d5 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -4,5 +4,5 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; public interface IEmployeeRepository { - Task ChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); + Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); } \ No newline at end of file From ea069a24f7b3e3827b06a6f21b278b789aa78a27 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 12:54:17 +0800 Subject: [PATCH 158/267] add collection trackable --- .../ChangeTrackingUnitTest.cs | 118 +++++++++++++++--- .../EmployeeAggregate/EmployeeAggregate.cs | 14 +-- .../Entity/EmployeeEntity.cs | 8 +- .../EmployeeAggregate/Entity/ProfileEntity.cs | 8 ++ .../Repository/EmployeeRepository.cs | 43 +++++++ .../AppDependencyInjectionExtensions.cs | 2 - .../EntityModel/EmployeeDbContext.cs | 2 - 7 files changed, 165 insertions(+), 30 deletions(-) create mode 100644 Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index cec066bb..a5f1efe1 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel; +using System.Linq; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; using ChangeTracking; using Lab.ChangeTracking.Domain.EmployeeAggregate; using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; @@ -15,11 +20,10 @@ namespace Lab.ChangeTracking.Domain.UnitTest; [TestClass] public class ChangeTrackingUnitTest { - private IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; - public ChangeTrackingUnitTest() - { - } + private readonly IDbContextFactory _employeeDbContextFactory = + TestAssistants.EmployeeDbContextFactory; [TestMethod] public void 原本用法() @@ -42,18 +46,92 @@ public void 追蹤() Id = Guid.NewGuid(), Name = "yao", Age = 12, + Identity = new IdentityEntity() { Account = "G1234" }, }; - var tracked = source.AsTrackable(); - tracked.Age = 18; + var trackable = source.AsTrackable(); + trackable.Name = "小章"; + var employTrackable = trackable.CastToIChangeTrackable(); - // source.Age = 18; - var trackable = tracked.CastToIChangeTrackable(); + var employeeChangedProperties = employTrackable.ChangedProperties.ToList(); - Assert.AreEqual(18, source.Age); + Console.WriteLine($"{nameof(this.追蹤)}:追蹤欄位"); + Console.WriteLine(JsonSerializer.Serialize(employeeChangedProperties)); + } + + [TestMethod] + public void 追蹤複雜型別() + { + var source = new EmployeeEntity() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + Identity = new IdentityEntity() { Account = "G1234" }, + }; + var trackable = source.AsTrackable(); + trackable.Name = "小章"; + trackable.Identity.Account = "yao"; + var employTrackable = trackable.CastToIChangeTrackable(); + var identityTrackable = trackable.Identity.CastToIChangeTrackable(); + + var employeeChangedProperties = employTrackable.ChangedProperties.ToList(); + var identityChangedProperties = identityTrackable.ChangedProperties.ToList(); + + Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤欄位"); + Console.WriteLine(JsonSerializer.Serialize(employeeChangedProperties)); + Console.WriteLine(JsonSerializer.Serialize(identityChangedProperties)); + } + + [TestMethod] + public void 追蹤集合() + { + var source = new EmployeeEntity() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + Identity = new IdentityEntity() { Account = "G1234" }, + Profiles = new List() + { + new() { FirstName = "第一筆" }, + new() { FirstName = "將被刪掉" }, + } + }; + var trackable = source.AsTrackable(); + trackable.Profiles[0].FirstName = "變更"; + trackable.Profiles.Add(new ProfileEntity() { FirstName = "新增" }); + trackable.Profiles.RemoveAt(1); + + var profileTrackable = trackable.Profiles.CastToIChangeTrackableCollection(); + + var unchangedItems = profileTrackable.UnchangedItems; + var addedItems = profileTrackable.AddedItems; + var changedItems = profileTrackable.ChangedItems; + var deleteItems = profileTrackable.DeletedItems; + + Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤集合"); + Console.WriteLine($"UnchangedItems:{Serialize(unchangedItems)}"); + Console.WriteLine($"AddItem:{Serialize(addedItems)}"); + Console.WriteLine($"ChangedItems:{Serialize(changedItems)}"); + Console.WriteLine($"DeleteItems:{Serialize(deleteItems)}"); + Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤變更屬性"); + var changeTrackable = trackable.Profiles[0].CastToIChangeTrackable(); + Console.WriteLine($"變更欄位:{Serialize(changeTrackable.ChangedProperties)}"); + } + + private static string Serialize(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions() + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; } [TestMethod] - public void 追蹤後異動() + public void 異動追蹤後存檔() { var toDB = Insert(); var source = new EmployeeEntity() @@ -61,11 +139,23 @@ public void 追蹤後異動() Id = toDB.Id, Name = "yao", Age = 12, - Identity = new IdentityEntity(){}, - Profiles = new Dictionary() - + Identity = new IdentityEntity(), }; var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; + this.DataShouldOk(source); + } + + private void DataShouldOk(EmployeeEntity source) + { + var dbContext = this._employeeDbContextFactory.CreateDbContext(); + var actual = dbContext.Employees + .Where(p => p.Id == source.Id) + .Include(p => p.Identity) + .First() + ; + + Assert.AreEqual("小章", actual.Name); + Assert.AreEqual("9528", actual.Identity.Password); } private static Employee Insert() @@ -90,6 +180,4 @@ private static Employee Insert() dbContext.SaveChanges(); return toDB; } - - } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index ea2db4f4..ed22f194 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -15,14 +15,12 @@ public EmployeeAggregate(IEmployeeRepository repository) public async Task ModifyFlowAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) { - var trackable = srcEmployee.AsTrackable(); + var memberTrackable = srcEmployee.AsTrackable(); + + memberTrackable.Name = "小章"; + memberTrackable.Identity.Password = "9527"; - trackable.Name = "小章"; - trackable.Identity.Password = "9527"; - // trackable.Profiles.Add("FirstName", "余"); - trackable.Profiles = new Dictionary() { { "Last", "小章" } }; - - var changeCount = await this._repository.SaveChangeAsync(trackable, cancel); - return trackable; + var changeCount = await this._repository.SaveChangeAsync(memberTrackable, cancel); + return memberTrackable; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index 651e77ee..29e79a09 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System.Collections.ObjectModel; +using System.ComponentModel; using System.Runtime.CompilerServices; using ChangeTracking; using Lab.ChangeTracking.Domain.Annotations; @@ -15,10 +16,11 @@ public class EmployeeEntity public virtual string Remark { get; set; } - public virtual Dictionary Profiles { get; set; } + public virtual IList Profiles { get; init; } + public virtual DateTimeOffset CreateAt { get; set; } public virtual string CreateBy { get; set; } - public virtual IdentityEntity Identity { get; set; } + public virtual IdentityEntity Identity { get; init; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs new file mode 100644 index 00000000..0a73b03d --- /dev/null +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; + +public class ProfileEntity +{ + public virtual string FirstName { get; set; } + + public virtual string LastName { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 2f424ad5..479d99cd 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -45,9 +45,50 @@ public async Task SaveChangeAsync(EmployeeEntity srcEmployee, { var employeeTrackable = srcEmployee.CastToIChangeTrackable(); var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); + var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); + var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); + + await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); + await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); + + try + { + var destEmployee = this.To(srcEmployee); + var memberChangeCount = await dbContext.Employees + .Where(a => a.Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee, + memberChangeProperties, cancel); + var identityChangeCount = await dbContext.Identities + .Where(a => a.Employee_Id == srcEmployee.Id) + .BatchUpdateAsync(destEmployee.Identity, + identityChangeProperties, + cancel); + + await transaction.CommitAsync(cancel); + return memberChangeCount + identityChangeCount; + } + catch (Exception e) + { + await transaction.RollbackAsync(cancel); + throw new Exception("存檔失敗"); + } + + return 0; + } + + public async Task SaveChange1Async(EmployeeEntity srcEmployee, + CancellationToken cancel = default) + { + var employeeTrackable = srcEmployee.CastToIChangeTrackable(); + var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); + var profileTrackable = srcEmployee.Profiles.CastToIChangeTrackableCollection(); var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); + var changedItems = profileTrackable.ChangedItems; + var addedItems = profileTrackable.AddedItems; + var unchangedItems = profileTrackable.UnchangedItems; + var deletedItems = profileTrackable.DeletedItems; await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); @@ -73,5 +114,7 @@ public async Task SaveChangeAsync(EmployeeEntity srcEmployee, await transaction.RollbackAsync(cancel); throw new Exception("存檔失敗"); } + + return 0; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs index b5cdcb8b..6fc51c5c 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -28,8 +28,6 @@ public static void AddEntityFramework(this IServiceCollection services) ; }); - ; - // services.AddPooledDbContextFactory((provider, options) => // { // var option = provider.GetService(); diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index db4d2c80..15a2302d 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -33,8 +33,6 @@ public EmployeeDbContext(DbContextOptions options) var sqlOptions = options.FindExtension(); if (sqlOptions != null) { - Console.WriteLine( - $"EmployeeDbContext of connection string be '{sqlOptions.ConnectionString}'"); this.Database.Migrate(); } } From 38cd599b55212e434ef4b3a420f65033817e6a92 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 13:40:35 +0800 Subject: [PATCH 159/267] class=>record --- .../ChangeTrackingUnitTest.cs | 2 +- .../EmployeeAggregate/Entity/EmployeeEntity.cs | 2 +- .../EmployeeAggregate/Entity/IdentityEntity.cs | 2 +- .../EmployeeAggregate/Entity/ProfileEntity.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index a5f1efe1..e4a4d8e5 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -155,7 +155,7 @@ private void DataShouldOk(EmployeeEntity source) ; Assert.AreEqual("小章", actual.Name); - Assert.AreEqual("9528", actual.Identity.Password); + Assert.AreEqual("9527", actual.Identity.Password); } private static Employee Insert() diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index 29e79a09..ac18ef3c 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -6,7 +6,7 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -public class EmployeeEntity +public record EmployeeEntity { public virtual Guid Id { get; init; } diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs index 48e24fed..09505d0f 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -1,6 +1,6 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -public class IdentityEntity +public record IdentityEntity { public virtual string Account { get; set; } diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs index 0a73b03d..9ae35128 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs @@ -1,6 +1,6 @@ namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -public class ProfileEntity +public record ProfileEntity { public virtual string FirstName { get; set; } From 0305f3aa1e63d2a09f2dd4d3101459c92f069dd0 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 13:44:06 +0800 Subject: [PATCH 160/267] refactor --- .../ChangeTrackingUnitTest.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index e4a4d8e5..c31d4a2e 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -52,10 +52,10 @@ public void 追蹤() trackable.Name = "小章"; var employTrackable = trackable.CastToIChangeTrackable(); - var employeeChangedProperties = employTrackable.ChangedProperties.ToList(); + var employeeChangedProperties = employTrackable.ChangedProperties; Console.WriteLine($"{nameof(this.追蹤)}:追蹤欄位"); - Console.WriteLine(JsonSerializer.Serialize(employeeChangedProperties)); + Console.WriteLine(ToJson(employeeChangedProperties)); } [TestMethod] @@ -78,8 +78,8 @@ public void 追蹤複雜型別() var identityChangedProperties = identityTrackable.ChangedProperties.ToList(); Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤欄位"); - Console.WriteLine(JsonSerializer.Serialize(employeeChangedProperties)); - Console.WriteLine(JsonSerializer.Serialize(identityChangedProperties)); + Console.WriteLine(ToJson(employeeChangedProperties)); + Console.WriteLine(ToJson(identityChangedProperties)); } [TestMethod] @@ -110,16 +110,16 @@ public void 追蹤集合() var deleteItems = profileTrackable.DeletedItems; Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤集合"); - Console.WriteLine($"UnchangedItems:{Serialize(unchangedItems)}"); - Console.WriteLine($"AddItem:{Serialize(addedItems)}"); - Console.WriteLine($"ChangedItems:{Serialize(changedItems)}"); - Console.WriteLine($"DeleteItems:{Serialize(deleteItems)}"); + Console.WriteLine($"UnchangedItems:{ToJson(unchangedItems)}"); + Console.WriteLine($"AddItem:{ToJson(addedItems)}"); + Console.WriteLine($"ChangedItems:{ToJson(changedItems)}"); + Console.WriteLine($"DeleteItems:{ToJson(deleteItems)}"); Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤變更屬性"); var changeTrackable = trackable.Profiles[0].CastToIChangeTrackable(); - Console.WriteLine($"變更欄位:{Serialize(changeTrackable.ChangedProperties)}"); + Console.WriteLine($"變更欄位:{ToJson(changeTrackable.ChangedProperties)}"); } - private static string Serialize(T instance) + private static string ToJson(T instance) { var serialize = JsonSerializer.Serialize(instance, new JsonSerializerOptions() From 66ca512fa482603fd55ae652f3b121a87cdd30d6 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 14:25:00 +0800 Subject: [PATCH 161/267] refactor --- .../ChangeTrackingUnitTest.cs | 87 +++++++++---------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index c31d4a2e..035d8d03 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; @@ -9,7 +7,6 @@ using ChangeTracking; using Lab.ChangeTracking.Domain.EmployeeAggregate; using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Lab.MultiTestCase.UnitTest; using Microsoft.EntityFrameworkCore; @@ -28,7 +25,7 @@ public class ChangeTrackingUnitTest [TestMethod] public void 原本用法() { - var source = new EmployeeEntity() + var source = new EmployeeEntity { Id = Guid.NewGuid(), Name = "yao", @@ -41,12 +38,12 @@ public void 原本用法() [TestMethod] public void 追蹤() { - var source = new EmployeeEntity() + var source = new EmployeeEntity { Id = Guid.NewGuid(), Name = "yao", Age = 12, - Identity = new IdentityEntity() { Account = "G1234" }, + Identity = new IdentityEntity { Account = "G1234" }, }; var trackable = source.AsTrackable(); trackable.Name = "小章"; @@ -58,40 +55,16 @@ public void 追蹤() Console.WriteLine(ToJson(employeeChangedProperties)); } - [TestMethod] - public void 追蹤複雜型別() - { - var source = new EmployeeEntity() - { - Id = Guid.NewGuid(), - Name = "yao", - Age = 12, - Identity = new IdentityEntity() { Account = "G1234" }, - }; - var trackable = source.AsTrackable(); - trackable.Name = "小章"; - trackable.Identity.Account = "yao"; - var employTrackable = trackable.CastToIChangeTrackable(); - var identityTrackable = trackable.Identity.CastToIChangeTrackable(); - - var employeeChangedProperties = employTrackable.ChangedProperties.ToList(); - var identityChangedProperties = identityTrackable.ChangedProperties.ToList(); - - Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤欄位"); - Console.WriteLine(ToJson(employeeChangedProperties)); - Console.WriteLine(ToJson(identityChangedProperties)); - } - [TestMethod] public void 追蹤集合() { - var source = new EmployeeEntity() + var source = new EmployeeEntity { Id = Guid.NewGuid(), Name = "yao", Age = 12, - Identity = new IdentityEntity() { Account = "G1234" }, - Profiles = new List() + Identity = new IdentityEntity { Account = "G1234" }, + Profiles = new List { new() { FirstName = "第一筆" }, new() { FirstName = "將被刪掉" }, @@ -99,7 +72,7 @@ public void 追蹤集合() }; var trackable = source.AsTrackable(); trackable.Profiles[0].FirstName = "變更"; - trackable.Profiles.Add(new ProfileEntity() { FirstName = "新增" }); + trackable.Profiles.Add(new ProfileEntity { FirstName = "新增" }); trackable.Profiles.RemoveAt(1); var profileTrackable = trackable.Profiles.CastToIChangeTrackableCollection(); @@ -119,22 +92,35 @@ public void 追蹤集合() Console.WriteLine($"變更欄位:{ToJson(changeTrackable.ChangedProperties)}"); } - private static string ToJson(T instance) + [TestMethod] + public void 追蹤複雜型別() { - var serialize = JsonSerializer.Serialize(instance, - new JsonSerializerOptions() - { - Encoder = JavaScriptEncoder.Create( - UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) - }); - return serialize; + var source = new EmployeeEntity + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 12, + Identity = new IdentityEntity { Account = "G1234" }, + }; + var trackable = source.AsTrackable(); + trackable.Name = "小章"; + trackable.Identity.Account = "yao"; + var employTrackable = trackable.CastToIChangeTrackable(); + var identityTrackable = trackable.Identity.CastToIChangeTrackable(); + + var employeeChangedProperties = employTrackable.ChangedProperties; + var identityChangedProperties = identityTrackable.ChangedProperties; + + Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤欄位"); + Console.WriteLine(ToJson(employeeChangedProperties)); + Console.WriteLine(ToJson(identityChangedProperties)); } [TestMethod] public void 異動追蹤後存檔() { var toDB = Insert(); - var source = new EmployeeEntity() + var source = new EmployeeEntity { Id = toDB.Id, Name = "yao", @@ -161,14 +147,14 @@ private void DataShouldOk(EmployeeEntity source) private static Employee Insert() { using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); - var toDB = new Employee() + var toDB = new Employee { Id = Guid.NewGuid(), Age = 18, Name = "yao", CreateAt = DateTimeOffset.Now, CreateBy = "TEST", - Identity = new Identity() + Identity = new Identity { Account = "yao", Password = "123456", @@ -180,4 +166,15 @@ private static Employee Insert() dbContext.SaveChanges(); return toDB; } + + private static string ToJson(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; + } } \ No newline at end of file From 98a592e43d452335ccacd1ebf60f18f51b95940d Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 15:20:13 +0800 Subject: [PATCH 162/267] refactor --- .../ChangeTrackingUnitTest.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 035d8d03..50807b93 100644 --- a/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -51,7 +51,7 @@ public void 追蹤() var employeeChangedProperties = employTrackable.ChangedProperties; - Console.WriteLine($"{nameof(this.追蹤)}:追蹤欄位"); + Console.WriteLine($"{nameof(this.追蹤)}:追蹤 Employee 欄位"); Console.WriteLine(ToJson(employeeChangedProperties)); } @@ -82,14 +82,14 @@ public void 追蹤集合() var changedItems = profileTrackable.ChangedItems; var deleteItems = profileTrackable.DeletedItems; - Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤集合"); + Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤 Profiles 集合"); Console.WriteLine($"UnchangedItems:{ToJson(unchangedItems)}"); Console.WriteLine($"AddItem:{ToJson(addedItems)}"); Console.WriteLine($"ChangedItems:{ToJson(changedItems)}"); Console.WriteLine($"DeleteItems:{ToJson(deleteItems)}"); - Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤變更屬性"); + Console.WriteLine($"{nameof(this.追蹤集合)}:追蹤 Profiles[0] 變更屬性"); var changeTrackable = trackable.Profiles[0].CastToIChangeTrackable(); - Console.WriteLine($"變更欄位:{ToJson(changeTrackable.ChangedProperties)}"); + Console.WriteLine($"Profiles[0] 變更欄位:{ToJson(changeTrackable.ChangedProperties)}"); } [TestMethod] @@ -111,8 +111,9 @@ public void 追蹤複雜型別() var employeeChangedProperties = employTrackable.ChangedProperties; var identityChangedProperties = identityTrackable.ChangedProperties; - Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤欄位"); + Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤 Employee 欄位"); Console.WriteLine(ToJson(employeeChangedProperties)); + Console.WriteLine($"{nameof(this.追蹤複雜型別)}:追蹤 Identity 欄位"); Console.WriteLine(ToJson(identityChangedProperties)); } From f955806c99999e61dd1440ea6c9f2d3a35aa12af Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 19 Feb 2022 22:25:12 +0800 Subject: [PATCH 163/267] add bddpipe project --- .../Lab.BDD.Pipe.TestProject.csproj | 18 +++++++++ .../Lab.BDD.Pipe.TestProject/UnitTest1.cs | 39 +++++++++++++++++++ Test/Lab.BDD.Pipe/Lab.BDD.Pipe.sln | 16 ++++++++ 3 files changed, 73 insertions(+) create mode 100644 Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/Lab.BDD.Pipe.TestProject.csproj create mode 100644 Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/UnitTest1.cs create mode 100644 Test/Lab.BDD.Pipe/Lab.BDD.Pipe.sln diff --git a/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/Lab.BDD.Pipe.TestProject.csproj b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/Lab.BDD.Pipe.TestProject.csproj new file mode 100644 index 00000000..1fdc25af --- /dev/null +++ b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/Lab.BDD.Pipe.TestProject.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + diff --git a/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/UnitTest1.cs b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/UnitTest1.cs new file mode 100644 index 00000000..ab3f3637 --- /dev/null +++ b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.TestProject/UnitTest1.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using BddPipe; +using BddPipe.Model; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static BddPipe.Runner; +using BddPipe.Recipe; + +namespace Lab.BDD.Pipe.TestProject; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void 相加兩個數字() + { + Scenario() + .Given("有兩個數字", () => new { firstNumber = (decimal)5, secondNumber = (decimal)10 }) + .When("按下相加", setup => + { + var calculation = new Calculation(); + return calculation.Add(setup.firstNumber, setup.secondNumber); + }) + .Then("預期得到", actual => + { + var expected = 15; + Assert.AreEqual(expected, actual); + }) + .Run(); + } +} + +public class Calculation +{ + public decimal Add(decimal firstNumber, decimal secondNumber) + { + return firstNumber + secondNumber; + } +} \ No newline at end of file diff --git a/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.sln b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.sln new file mode 100644 index 00000000..4b518bf3 --- /dev/null +++ b/Test/Lab.BDD.Pipe/Lab.BDD.Pipe.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.BDD.Pipe.TestProject", "Lab.BDD.Pipe.TestProject\Lab.BDD.Pipe.TestProject.csproj", "{737A0583-DB1E-4C63-8552-EE6ED19700A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {737A0583-DB1E-4C63-8552-EE6ED19700A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {737A0583-DB1E-4C63-8552-EE6ED19700A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {737A0583-DB1E-4C63-8552-EE6ED19700A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {737A0583-DB1E-4C63-8552-EE6ED19700A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From c65d3532e791896b39426d4afb105ed44439d45e Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 28 Feb 2022 02:41:03 +0800 Subject: [PATCH 164/267] add first project --- .../ChangeTrackingForORM.sln | 48 +++++++ .../ChangeTrackingForORM/Makefile | 4 + .../ChangeTrackingForORM/docker-compose.yml | 10 ++ .../Employee/IEmployeeEntity.cs | 17 +++ .../Employee/IIdentityEntity.cs | 10 ++ .../Employee/IProfileEntity.cs | 8 ++ .../IChangeState.cs | 6 + .../IChangeTime.cs | 12 ++ .../IChangeTrackable.cs | 5 + .../Lab.ChangeTracking.Abstract.csproj | 9 ++ .../ChangeTrackingUnitTest.cs | 74 ++++++++++ .../Lab.ChangeTracking.Domain.UnitTest.csproj | 28 ++++ .../MsTestHook.cs | 31 +++++ .../TestAssistants.cs | 48 +++++++ .../AggregationRoot.cs | 130 ++++++++++++++++++ .../EmployeeAggregate/EmployeeAggregate.cs | 21 +++ .../Entity/EmployeeEntity.cs | 28 ++++ .../Entity/IdentityEntity.cs | 14 ++ .../EmployeeAggregate/Entity/ProfileEntity.cs | 8 ++ .../EmployeeAggregate/IEmployeeAggregate.cs | 8 ++ .../Repository/EmployeeRepository.cs | 120 ++++++++++++++++ .../Repository/IEmployeeRepository.cs | 8 ++ .../Lab.ChangeTracking.Domain/EntityState.cs | 9 ++ .../src/Lab.ChangeTracking.Domain/Error.cs | 2 + .../Lab.ChangeTracking.Domain.csproj | 20 +++ .../AppDependencyInjectionExtensions.cs | 50 +++++++ .../AppEnvironmentOption.cs | 31 +++++ .../EntityModel/Employee.cs | 35 +++++ .../EntityModel/EmployeeDbContext.cs | 89 ++++++++++++ .../EntityModel/Identity.cs | 34 +++++ .../EntityModel/Profile.cs | 20 +++ .../EnvironmentAssistant.cs | 15 ++ ...ab.ChangeTracking.Infrastructure.DB.csproj | 18 +++ 33 files changed, 970 insertions(+) create mode 100644 Property Change Tracking/ChangeTrackingForORM/ChangeTrackingForORM.sln create mode 100644 Property Change Tracking/ChangeTrackingForORM/Makefile create mode 100644 Property Change Tracking/ChangeTrackingForORM/docker-compose.yml create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IEmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IIdentityEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeState.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTime.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTrackable.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Error.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj diff --git a/Property Change Tracking/ChangeTrackingForORM/ChangeTrackingForORM.sln b/Property Change Tracking/ChangeTrackingForORM/ChangeTrackingForORM.sln new file mode 100644 index 00000000..783f6db4 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/ChangeTrackingForORM.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Infrastructure.DB", "src\Lab.ChangeTracking.Infrastructure.DB\Lab.ChangeTracking.Infrastructure.DB.csproj", "{A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain", "src\Lab.ChangeTracking.Domain\Lab.ChangeTracking.Domain.csproj", "{A1C827F2-683E-470C-A3DE-BD6DD2BE5198}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain.UnitTest", "src\Lab.ChangeTracking.Domain.UnitTest\Lab.ChangeTracking.Domain.UnitTest.csproj", "{7066EE2C-28A8-4408-B212-E582608AB967}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Abstract", "src\Lab.ChangeTracking.Abstract\Lab.ChangeTracking.Abstract.csproj", "{28D1EF37-8DEA-47BD-83A1-4302CC590791}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.Build.0 = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.Build.0 = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.Build.0 = Release|Any CPU + {28D1EF37-8DEA-47BD-83A1-4302CC590791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28D1EF37-8DEA-47BD-83A1-4302CC590791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28D1EF37-8DEA-47BD-83A1-4302CC590791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28D1EF37-8DEA-47BD-83A1-4302CC590791}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {7066EE2C-28A8-4408-B212-E582608AB967} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {28D1EF37-8DEA-47BD-83A1-4302CC590791} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Property Change Tracking/ChangeTrackingForORM/Makefile b/Property Change Tracking/ChangeTrackingForORM/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/docker-compose.yml b/Property Change Tracking/ChangeTrackingForORM/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IEmployeeEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IEmployeeEntity.cs new file mode 100644 index 00000000..5d91da43 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IEmployeeEntity.cs @@ -0,0 +1,17 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IEmployeeEntity : IChangeTrackable +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public int? Age { get; set; } + + public string Remark { get; set; } + + // public IList Profiles { get; set; } + // + // public IIdentityEntity Identity { get; set; } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IIdentityEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IIdentityEntity.cs new file mode 100644 index 00000000..01defb52 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IIdentityEntity.cs @@ -0,0 +1,10 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IIdentityEntity : IChangeTime +{ + public string Account { get; set; } + + public string Password { get; set; } + + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs new file mode 100644 index 00000000..a7dddbc8 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IProfileEntity : IChangeTime +{ + public string FirstName { get; set; } + + public string LastName { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeState.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeState.cs new file mode 100644 index 00000000..c906d4ef --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeState.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IChangeState +{ + int Version { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTime.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTime.cs new file mode 100644 index 00000000..2bb10097 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTime.cs @@ -0,0 +1,12 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IChangeTime +{ + DateTimeOffset CreatedAt { get; set; } + + string CreatedBy { get; set; } + + DateTimeOffset UpdatedAt { get; set; } + + string UpdatedBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTrackable.cs new file mode 100644 index 00000000..24c9736f --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/IChangeTrackable.cs @@ -0,0 +1,5 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IChangeTrackable : IChangeTime, IChangeState +{ +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs new file mode 100644 index 00000000..8c63ed3d --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using ChangeTracking; +using Lab.ChangeTracking.Domain.Entity; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Lab.MultiTestCase.UnitTest; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class ChangeTrackingUnitTest +{ + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + + private readonly IDbContextFactory _employeeDbContextFactory = + TestAssistants.EmployeeDbContextFactory; + + [TestMethod] + public void 原本用法() + { + var employeeAggregate = new EmployeeAggregate(new EmployeeEntity() + { + Id = Guid.NewGuid(), + Age = 18, + Name = "Yao", + Remark = "TEST" + }); + employeeAggregate.SetName("小章"); + employeeAggregate.SubmitChange(DateTimeOffset.Now, "Sys1"); + + } + + private static Employee Insert() + { + Console.WriteLine("新增資料"); + using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var toDB = new Employee + { + Id = Guid.NewGuid(), + Age = 18, + Name = "yao", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "TEST", + Identity = new Identity + { + Account = "yao", + Password = "123456", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "TEST", + } + }; + dbContext.Employees.Add(toDB); + dbContext.SaveChanges(); + + return toDB; + } + + private static string ToJson(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj new file mode 100644 index 00000000..72a505c5 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..ca75b47b --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MultiTestCase.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs new file mode 100644 index 00000000..7a127ed1 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -0,0 +1,48 @@ +using System; +using Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Domain.Repository; +using Lab.ChangeTracking.Infrastructure.DB; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.MultiTestCase.UnitTest; + +// assistant +internal class TestAssistants +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + public static IEmployeeRepository EmployeeRepository => + _serviceProvider.GetService(); + + public static IEmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService(); + + static TestAssistants() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + SetTestEnvironmentVariable(); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + + // services.AddSingleton(); + // services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs new file mode 100644 index 00000000..07fc92db --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs @@ -0,0 +1,130 @@ +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Domain; + +public abstract class AggregationRoot where T : IChangeTrackable +{ + public EntityState State { get; private set; } + + /// + /// 建立時間 + /// + public DateTimeOffset CreatedAt + { + get => this._instance.CreatedAt; + init => this._instance.CreatedAt = value; + } + + /// + /// 建立者 + /// + public string CreatedBy + { + get => this._instance.CreatedBy; + init => this._instance.CreatedBy = value; + } + + /// + /// 異動時間 + /// + public DateTimeOffset UpdatedAt + { + get => this._instance.UpdatedAt; + init => this._instance.UpdatedAt = value; + } + + /// + /// 異動者 + /// + public string UpdatedBy + { + get => this._instance.UpdatedBy; + init => this._instance.UpdatedBy = value; + } + + /// + /// 異動版號 + /// + public int Version + { + get => this._instance.Version; + init => this._instance.Version = value; + } + + private readonly IList> _changes = new List>(); + protected readonly Dictionary ChangedProperties = new(); + protected readonly Dictionary OriginalValues = new(); + + protected T _instance; + + public AggregationRoot(T instance) + { + this._instance = instance; + } + + public IReadOnlyList> GetInstanceChanges() + { + return this._changes.ToList(); + } + + public T GetInstance() + { + return this._instance; + } + + /// + /// SubmitChange 後則進版號 + /// + /// + /// + /// + public (Error err, bool changed) SubmitChange(DateTimeOffset when, string who) + { + if (this.State == EntityState.Submitted) + { + return ( + new Error("STATE_CONFLICT", + $"Entity({this.State}) was submitted and should not submit again."), false); + } + + if (this.State == EntityState.Unchanged) + { + return (null, false); + } + + if (this.State == EntityState.Added) + { + this.Track(x => x.CreatedAt = when); + this.Track(x => x.CreatedBy = who); + this.Track(x => x.UpdatedAt = when); + this.Track(x => x.UpdatedBy = who); + this.Track(x => x.Version += 1); + } + else + { + this.Track(x => x.UpdatedAt = when); + this.Track(x => x.UpdatedBy = who); + this.Track(x => x.Version += 1); + } + + this.State = EntityState.Submitted; + + return (null, true); + } + + protected void Track(Action change) + { + if (this.State == EntityState.Submitted) + { + throw new Exception("已經 Submitted 的 Doamin,無法再進行修改。"); + } + + change(this._instance); + this._changes.Add(change); + + if (this.State == EntityState.Unchanged) + { + this.State = EntityState.Modified; + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs new file mode 100644 index 00000000..a526348e --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -0,0 +1,21 @@ +using Lab.ChangeTracking.Domain.Entity; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeAggregate : AggregationRoot +{ + public EmployeeAggregate(EmployeeEntity instance) : base(instance) + { + } + + public EmployeeAggregate SetName(string name) + { + var instance = this.GetInstance(); + if (instance.Name != name) + { + this.Track(p => p.Name = name); + } + + return this; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs new file mode 100644 index 00000000..c182ab19 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -0,0 +1,28 @@ +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Domain.Entity; + +public class EmployeeEntity :IEmployeeEntity +{ + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset UpdatedAt { get; set; } + + public string UpdatedBy { get; set; } + + public int Version { get; set; } + + public Guid Id { get; set; } + + public string Name { get; set; } + + public int? Age { get; set; } + + public string Remark { get; set; } + + // public IList Profiles { get; set; } + + public IdentityEntity Identity { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs new file mode 100644 index 00000000..457beb06 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain.Entity; + +public record IdentityEntity +{ + public virtual string Account { get; set; } + + public virtual string Password { get; set; } + + public virtual string Remark { get; set; } + + public virtual DateTimeOffset CreateAt { get; set; } + + public virtual string CreateBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs new file mode 100644 index 00000000..88550e4b --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain.Entity; + +public record ProfileEntity +{ + public virtual string FirstName { get; set; } + + public virtual string LastName { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..afe51a7e --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -0,0 +1,8 @@ +using Lab.ChangeTracking.Domain.Entity; + +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeAggregate +{ + Task ModifyFlowAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs new file mode 100644 index 00000000..2594db6d --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -0,0 +1,120 @@ +// using ChangeTracking; +// using EFCore.BulkExtensions; +// using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +// using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +// using Microsoft.EntityFrameworkCore; +// +// namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; +// +// public class EmployeeRepository : IEmployeeRepository +// { +// private readonly IDbContextFactory _memberContextFactory; +// +// public EmployeeRepository(IDbContextFactory memberContextFactory) +// { +// this._memberContextFactory = memberContextFactory; +// } +// +// public Employee To(EmployeeEntity srcEmployee) +// { +// return new Employee +// { +// Id = srcEmployee.Id, +// Name = srcEmployee.Name, +// Age = srcEmployee.Age, +// Remark = srcEmployee.Remark, +// CreateAt = srcEmployee.CreateAt, +// CreateBy = srcEmployee.CreateBy, +// Identity = this.To(srcEmployee.Identity) +// }; +// } +// +// public Identity To(IdentityEntity srcIdentity) +// { +// return new Identity +// { +// Password = srcIdentity.Password, +// Remark = srcIdentity.Remark, +// CreateAt = srcIdentity.CreateAt, +// CreateBy = srcIdentity.CreateBy, +// }; +// } +// +// public async Task SaveChangeAsync(EmployeeEntity srcEmployee, +// CancellationToken cancel = default) +// { +// var employeeTrackable = srcEmployee.CastToIChangeTrackable(); +// var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); +// var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); +// var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); +// +// await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); +// await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); +// +// try +// { +// var destEmployee = this.To(srcEmployee); +// var memberChangeCount = await dbContext.Employees +// .Where(a => a.Id == srcEmployee.Id) +// .BatchUpdateAsync(destEmployee, +// memberChangeProperties, cancel); +// var identityChangeCount = await dbContext.Identities +// .Where(a => a.Employee_Id == srcEmployee.Id) +// .BatchUpdateAsync(destEmployee.Identity, +// identityChangeProperties, +// cancel); +// +// await transaction.CommitAsync(cancel); +// return memberChangeCount + identityChangeCount; +// } +// catch (Exception e) +// { +// await transaction.RollbackAsync(cancel); +// throw new Exception("存檔失敗"); +// } +// +// return 0; +// } +// +// public async Task SaveChange1Async(EmployeeEntity srcEmployee, +// CancellationToken cancel = default) +// { +// var employeeTrackable = srcEmployee.CastToIChangeTrackable(); +// var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); +// var profileTrackable = srcEmployee.Profiles.CastToIChangeTrackableCollection(); +// +// var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); +// var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); +// var changedItems = profileTrackable.ChangedItems; +// var addedItems = profileTrackable.AddedItems; +// var unchangedItems = profileTrackable.UnchangedItems; +// var deletedItems = profileTrackable.DeletedItems; +// +// await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); +// await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); +// +// try +// { +// var destEmployee = this.To(srcEmployee); +// var memberChangeCount = await dbContext.Employees +// .Where(a => a.Id == srcEmployee.Id) +// .BatchUpdateAsync(destEmployee, +// memberChangeProperties, cancel); +// var identityChangeCount = await dbContext.Identities +// .Where(a => a.Employee_Id == srcEmployee.Id) +// .BatchUpdateAsync(destEmployee.Identity, +// identityChangeProperties, +// cancel); +// +// await transaction.CommitAsync(cancel); +// return memberChangeCount + identityChangeCount; +// } +// catch (Exception e) +// { +// await transaction.RollbackAsync(cancel); +// throw new Exception("存檔失敗"); +// } +// +// return 0; +// } +// } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..0564926a --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -0,0 +1,8 @@ +using Lab.ChangeTracking.Domain.Entity; + +namespace Lab.ChangeTracking.Domain.Repository; + +public interface IEmployeeRepository +{ + Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs new file mode 100644 index 00000000..7b4c0e5c --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs @@ -0,0 +1,9 @@ +namespace Lab.ChangeTracking.Domain; + +public enum EntityState +{ + Added, + Unchanged, + Modified, + Submitted +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Error.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Error.cs new file mode 100644 index 00000000..7dbcb965 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Error.cs @@ -0,0 +1,2 @@ +namespace Lab.ChangeTracking.Domain; +public record Error(T Code, object Message); \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj new file mode 100644 index 00000000..def7c560 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..6fc51c5c --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,50 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ChangeTracking.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => + { + builder.AddConsole(); + }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..e29f53f3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,31 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private string _employeeDbConnectionString; + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..42cac8ca --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Employee : IEmployeeEntity + { + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + public string Remark { get; set; } + + public IList Profiles { get; set; } + + public Identity Identity { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset UpdatedAt { get; set; } + + public string UpdatedBy { get; set; } + + public int Version { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..80e699a6 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,89 @@ +using System.Dynamic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet Profiles { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + this.Database.Migrate(); + } + + s_migrated[0] = true; + } + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); + modelBuilder.ApplyConfiguration(new IdentityConfiguration()); + } + + internal class EmployeeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Employee"); + builder.HasKey(p => p.Id) + .IsClustered(false); + + builder.Property(p => p.Name).IsRequired(true); + builder.Property(p => p.Remark).IsRequired(false); + builder.Property(p => p.UpdatedBy).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + + internal class IdentityConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Identity"); + builder.HasKey(p => p.Employee_Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithOne(p => p.Identity) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade) + ; + + builder.Property(p => p.Account).IsRequired(); + builder.Property(p => p.Password).IsRequired(); + builder.Property(p => p.Remark).IsRequired(false); + builder.Property(p => p.UpdatedBy).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..62b36ba3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Security.Principal; +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Identity : IIdentityEntity + { + public Guid Employee_Id { get; set; } + + // [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset UpdatedAt { get; set; } + + public string UpdatedBy { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs new file mode 100644 index 00000000..20ed9ff4 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs @@ -0,0 +1,20 @@ +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +public class Profile : IProfileEntity +{ + public Guid Id { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset UpdatedAt { get; set; } + + public string UpdatedBy { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..b115d8a2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj new file mode 100644 index 00000000..d944728b --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + From 066023b6b3f75659860a99ddcbaaeffabd15b9bd Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 2 Mar 2022 01:04:28 +0800 Subject: [PATCH 165/267] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E7=95=B0?= =?UTF-8?q?=E5=8B=95=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Employee/IProfileEntity.cs | 2 + .../ChangeTrackingUnitTest.cs | 74 ++++-- .../Lab.ChangeTracking.Domain.UnitTest.csproj | 1 + .../TestAssistants.cs | 14 +- .../AggregationRoot.cs | 51 ++-- .../EmployeeAggregate/EmployeeAggregate.cs | 71 +++++- .../Entity/EmployeeEntity.cs | 2 +- .../EmployeeAggregate/IEmployeeAggregate.cs | 11 +- .../Repository/EmployeeRepository.cs | 219 ++++++++---------- .../Repository/IEmployeeRepository.cs | 9 +- .../EmployeeRepositoryExtensions.cs | 22 ++ .../Lab.ChangeTracking.Domain/EntityState.cs | 8 +- .../IAggregationRoot.cs | 22 ++ .../Lab.ChangeTracking.Domain/ISystemClock.cs | 11 + .../IUUIDProvider.cs | 14 ++ .../EntityModel/EmployeeDbContext.cs | 62 +++-- .../EntityModel/Profile.cs | 8 + 17 files changed, 409 insertions(+), 192 deletions(-) create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/ISystemClock.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IUUIDProvider.cs diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs index a7dddbc8..f0acfa1d 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Abstract/Employee/IProfileEntity.cs @@ -5,4 +5,6 @@ public interface IProfileEntity : IChangeTime public string FirstName { get; set; } public string LastName { get; set; } + + public string Remark { get; set; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 8c63ed3d..5b9e1835 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,48 +1,85 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Unicode; -using ChangeTracking; -using Lab.ChangeTracking.Domain.Entity; +using System.Threading.Tasks; +using Lab.ChangeTracking.Abstract; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Lab.MultiTestCase.UnitTest; using Microsoft.EntityFrameworkCore; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; namespace Lab.ChangeTracking.Domain.UnitTest; [TestClass] public class ChangeTrackingUnitTest { - private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; private readonly IDbContextFactory _employeeDbContextFactory = TestAssistants.EmployeeDbContextFactory; [TestMethod] - public void 原本用法() + public async Task 新增() { - var employeeAggregate = new EmployeeAggregate(new EmployeeEntity() - { - Id = Guid.NewGuid(), - Age = 18, - Name = "Yao", - Remark = "TEST" - }); - employeeAggregate.SetName("小章"); - employeeAggregate.SubmitChange(DateTimeOffset.Now, "Sys1"); - + // arrange + var employeeRepository = TestAssistants.EmployeeRepository; + var systemClock = Substitute.For(); + systemClock.Now.Returns(DateTimeOffset.Parse("2021-01-01")); + var uuIdProvider = Substitute.For(); + uuIdProvider.GenerateId().Returns(TestAssistants.Parse("1")); + + // act + var employeeAggregate = new EmployeeAggregate(employeeRepository, uuIdProvider, systemClock); + employeeAggregate.CreateInstance("yao", 18, "Test User"); + employeeAggregate.SubmitChange(systemClock.Now, "Sys1"); + var result = await employeeAggregate.SaveChangeAsync(); + + // assert + await using var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(); + var actual = dbContext.Employees.AsTracking().FirstOrDefault(); + Assert.AreEqual("yao", actual.Name); + Assert.AreEqual(18, actual.Age); + Assert.AreEqual(1, actual.Version); + Assert.AreEqual("Test User", actual.Remark); + } + + [TestMethod] + public async Task 編輯() + { + // arrange + InsertTestData(); + var employeeRepository = TestAssistants.EmployeeRepository; + var systemClock = Substitute.For(); + systemClock.Now.Returns(DateTimeOffset.Parse("2021-01-02")); + var uuIdProvider = Substitute.For(); + uuIdProvider.GenerateId().Returns(TestAssistants.Parse("1")); + + // act + var employeeAggregate = new EmployeeAggregate(employeeRepository, uuIdProvider, systemClock); + await employeeAggregate.GetAsync(TestAssistants.Parse("1")); + employeeAggregate.SetName("小章"); + employeeAggregate.SetAge(20); + employeeAggregate.SubmitChange(systemClock.Now, "Sys1"); + var count = await employeeAggregate.SaveChangeAsync(); + + // assert + await using var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(); + var actual = dbContext.Employees.AsTracking().FirstOrDefault(); + Assert.AreEqual("小章", actual.Name); + Assert.AreEqual(20, actual.Age); + Assert.AreEqual(2, actual.Version); } - private static Employee Insert() + private static Employee InsertTestData() { Console.WriteLine("新增資料"); using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); var toDB = new Employee { - Id = Guid.NewGuid(), + Id = TestAssistants.Parse("1"), Age = 18, Name = "yao", CreatedAt = DateTimeOffset.Now, @@ -53,7 +90,8 @@ private static Employee Insert() Password = "123456", CreatedAt = DateTimeOffset.Now, CreatedBy = "TEST", - } + }, + Version = 1 }; dbContext.Employees.Add(toDB); dbContext.SaveChanges(); diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj index 72a505c5..b29d606e 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -19,6 +19,7 @@ +
diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs index 7a127ed1..dd355049 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -1,4 +1,5 @@ using System; +using Lab.ChangeTracking.Abstract; using Lab.ChangeTracking.Domain; using Lab.ChangeTracking.Domain.Repository; using Lab.ChangeTracking.Infrastructure.DB; @@ -19,8 +20,8 @@ internal class TestAssistants public static IEmployeeRepository EmployeeRepository => _serviceProvider.GetService(); - public static IEmployeeAggregate EmployeeAggregate => - _serviceProvider.GetService(); + public static IEmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService>(); static TestAssistants() { @@ -34,7 +35,7 @@ public static void ConfigureTestServices(IServiceCollection services) services.AddAppEnvironment(); services.AddEntityFramework(); - // services.AddSingleton(); + services.AddSingleton(); // services.AddSingleton(); _serviceProvider = services.BuildServiceProvider(); } @@ -45,4 +46,11 @@ public static void SetTestEnvironmentVariable() option.EmployeeDbConnectionString = "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; } + public static Guid Parse(string id) + { + var guidFormat = "{0}-0000-0000-0000-000000000000"; + var guidText = string.Format(guidFormat, id.PadRight(8, '0')); + var key = Guid.Parse(guidText); + return key; + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs index 07fc92db..6dc74137 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs @@ -2,9 +2,9 @@ namespace Lab.ChangeTracking.Domain; -public abstract class AggregationRoot where T : IChangeTrackable +public abstract class AggregationRoot : IAggregationRoot where T : IChangeTrackable { - public EntityState State { get; private set; } + public EntityState State { get; protected set; } /// /// 建立時間 @@ -51,25 +51,35 @@ public int Version init => this._instance.Version = value; } - private readonly IList> _changes = new List>(); + private readonly IList> _changeActions = new List>(); protected readonly Dictionary ChangedProperties = new(); protected readonly Dictionary OriginalValues = new(); protected T _instance; - public AggregationRoot(T instance) + public IReadOnlyList> GetChangeActions() { - this._instance = instance; + return this._changeActions.ToList(); } - public IReadOnlyList> GetInstanceChanges() + public void SetInstance(T instance) { - return this._changes.ToList(); + this.State = EntityState.Unchanged; + this._instance = instance; } public T GetInstance() { return this._instance; + + // return new T + // { + // Version = _instance.Version, + // CreatedAt = _instance.CreatedAt, + // CreatedBy = this._instance.CreatedBy, + // UpdatedAt = this.UpdatedAt, + // UpdatedBy = this.CreatedBy + // }; } /// @@ -94,17 +104,17 @@ public T GetInstance() if (this.State == EntityState.Added) { - this.Track(x => x.CreatedAt = when); - this.Track(x => x.CreatedBy = who); - this.Track(x => x.UpdatedAt = when); - this.Track(x => x.UpdatedBy = who); - this.Track(x => x.Version += 1); + this.ChangeTrack(x => x.CreatedAt = when); + this.ChangeTrack(x => x.CreatedBy = who); + this.ChangeTrack(x => x.UpdatedAt = when); + this.ChangeTrack(x => x.UpdatedBy = who); + this.ChangeTrack(x => x.Version += 1); } else { - this.Track(x => x.UpdatedAt = when); - this.Track(x => x.UpdatedBy = who); - this.Track(x => x.Version += 1); + this.ChangeTrack(x => x.UpdatedAt = when); + this.ChangeTrack(x => x.UpdatedBy = who); + this.ChangeTrack(x => x.Version += 1); } this.State = EntityState.Submitted; @@ -112,19 +122,24 @@ public T GetInstance() return (null, true); } - protected void Track(Action change) + public void ChangeTrack(Action changeAction) { if (this.State == EntityState.Submitted) { throw new Exception("已經 Submitted 的 Doamin,無法再進行修改。"); } - change(this._instance); - this._changes.Add(change); + changeAction(this._instance); + this._changeActions.Add(changeAction); if (this.State == EntityState.Unchanged) { this.State = EntityState.Modified; } } + + private T Clone(T source) + { + return (T)Activator.CreateInstance(typeof(T)); + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index a526348e..18a2e0a4 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -1,19 +1,82 @@ +using Lab.ChangeTracking.Abstract; using Lab.ChangeTracking.Domain.Entity; +using Lab.ChangeTracking.Domain.Repository; namespace Lab.ChangeTracking.Domain; -public class EmployeeAggregate : AggregationRoot +// public class EmployeeAggregate : AggregationRoot, +// IEmployeeAggregate +public class EmployeeAggregate : AggregationRoot { - public EmployeeAggregate(EmployeeEntity instance) : base(instance) + public Guid Id => this._instance.Id; + + public string Name => this._instance.Name; + + public int? Age => this._instance.Age; + + public string Remark => this._instance.Remark; + + private readonly IEmployeeRepository _repository; + private readonly ISystemClock _systemClock; + private readonly IUUIdProvider _uuIdProvider; + + private string _name; + + public EmployeeAggregate(IEmployeeRepository repository, + IUUIdProvider uuIdProvider, + ISystemClock systemClock) + { + this._repository = repository; + this._uuIdProvider = uuIdProvider; + this._systemClock = systemClock; + } + + public void CreateInstance(string name, int age, string remark = null) + { + var now = this._systemClock.Now; + var instance = new EmployeeEntity + { + Id = this._uuIdProvider.GenerateId(), + Name = name, + Age = age, + Version = 1, + Remark = remark, + Identity = null + }; + this.State = EntityState.Added; + this._instance = instance; + } + + public async Task GetAsync(Guid id, CancellationToken cancel = default) + { + this._instance = await this._repository.GetAsync(id, cancel); + + this.State = EntityState.Unchanged; + } + + public async Task SaveChangeAsync(CancellationToken cancel = default) + { + return await this._repository.SaveChangeAsync(this, cancel); + } + + public EmployeeAggregate SetAge(int age) { + var instance = this._instance; + if (instance.Age != age) + { + this.ChangeTrack(p => p.Age = age); + } + + return this; } + // public IEmployeeAggregate SetName(string name) public EmployeeAggregate SetName(string name) { - var instance = this.GetInstance(); + var instance = this._instance; if (instance.Name != name) { - this.Track(p => p.Name = name); + this.ChangeTrack(p => p.Name = name); } return this; diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index c182ab19..197bb5c8 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -2,7 +2,7 @@ namespace Lab.ChangeTracking.Domain.Entity; -public class EmployeeEntity :IEmployeeEntity +public record EmployeeEntity : IEmployeeEntity { public DateTimeOffset CreatedAt { get; set; } diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs index afe51a7e..986572dc 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -1,8 +1,13 @@ -using Lab.ChangeTracking.Domain.Entity; +using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Domain.Entity; namespace Lab.ChangeTracking.Domain; -public interface IEmployeeAggregate +public interface IEmployeeAggregate : IAggregationRoot where T : IChangeTrackable { - Task ModifyFlowAsync(EmployeeEntity employee, CancellationToken cancel = default); + IEmployeeAggregate SetName(string name); + + IEmployeeAggregate SetAge(int age); + + void SaveChangeAsync(CancellationToken cancel = default); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 2594db6d..c42e8103 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -1,120 +1,99 @@ -// using ChangeTracking; -// using EFCore.BulkExtensions; -// using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; -// using Lab.ChangeTracking.Infrastructure.DB.EntityModel; -// using Microsoft.EntityFrameworkCore; -// -// namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; -// -// public class EmployeeRepository : IEmployeeRepository -// { -// private readonly IDbContextFactory _memberContextFactory; -// -// public EmployeeRepository(IDbContextFactory memberContextFactory) -// { -// this._memberContextFactory = memberContextFactory; -// } -// -// public Employee To(EmployeeEntity srcEmployee) -// { -// return new Employee -// { -// Id = srcEmployee.Id, -// Name = srcEmployee.Name, -// Age = srcEmployee.Age, -// Remark = srcEmployee.Remark, -// CreateAt = srcEmployee.CreateAt, -// CreateBy = srcEmployee.CreateBy, -// Identity = this.To(srcEmployee.Identity) -// }; -// } -// -// public Identity To(IdentityEntity srcIdentity) -// { -// return new Identity -// { -// Password = srcIdentity.Password, -// Remark = srcIdentity.Remark, -// CreateAt = srcIdentity.CreateAt, -// CreateBy = srcIdentity.CreateBy, -// }; -// } -// -// public async Task SaveChangeAsync(EmployeeEntity srcEmployee, -// CancellationToken cancel = default) -// { -// var employeeTrackable = srcEmployee.CastToIChangeTrackable(); -// var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); -// var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); -// var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); -// -// await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); -// await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); -// -// try -// { -// var destEmployee = this.To(srcEmployee); -// var memberChangeCount = await dbContext.Employees -// .Where(a => a.Id == srcEmployee.Id) -// .BatchUpdateAsync(destEmployee, -// memberChangeProperties, cancel); -// var identityChangeCount = await dbContext.Identities -// .Where(a => a.Employee_Id == srcEmployee.Id) -// .BatchUpdateAsync(destEmployee.Identity, -// identityChangeProperties, -// cancel); -// -// await transaction.CommitAsync(cancel); -// return memberChangeCount + identityChangeCount; -// } -// catch (Exception e) -// { -// await transaction.RollbackAsync(cancel); -// throw new Exception("存檔失敗"); -// } -// -// return 0; -// } -// -// public async Task SaveChange1Async(EmployeeEntity srcEmployee, -// CancellationToken cancel = default) -// { -// var employeeTrackable = srcEmployee.CastToIChangeTrackable(); -// var identityTrackable = srcEmployee.Identity.CastToIChangeTrackable(); -// var profileTrackable = srcEmployee.Profiles.CastToIChangeTrackableCollection(); -// -// var memberChangeProperties = employeeTrackable.ChangedProperties.ToList(); -// var identityChangeProperties = identityTrackable.ChangedProperties.ToList(); -// var changedItems = profileTrackable.ChangedItems; -// var addedItems = profileTrackable.AddedItems; -// var unchangedItems = profileTrackable.UnchangedItems; -// var deletedItems = profileTrackable.DeletedItems; -// -// await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); -// await using var transaction = await dbContext.Database.BeginTransactionAsync(cancel); -// -// try -// { -// var destEmployee = this.To(srcEmployee); -// var memberChangeCount = await dbContext.Employees -// .Where(a => a.Id == srcEmployee.Id) -// .BatchUpdateAsync(destEmployee, -// memberChangeProperties, cancel); -// var identityChangeCount = await dbContext.Identities -// .Where(a => a.Employee_Id == srcEmployee.Id) -// .BatchUpdateAsync(destEmployee.Identity, -// identityChangeProperties, -// cancel); -// -// await transaction.CommitAsync(cancel); -// return memberChangeCount + identityChangeCount; -// } -// catch (Exception e) -// { -// await transaction.RollbackAsync(cancel); -// throw new Exception("存檔失敗"); -// } -// -// return 0; -// } -// } \ No newline at end of file +using ChangeTracking; +using EFCore.BulkExtensions; +using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Domain.Entity; +using Lab.ChangeTracking.Domain.Repository; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain.Repository; + +public class EmployeeRepository : IEmployeeRepository +{ + private readonly IDbContextFactory _employeeContextFactory; + + public EmployeeRepository(IDbContextFactory employeeContextFactory) + { + this._employeeContextFactory = employeeContextFactory; + } + + public Employee To(EmployeeEntity srcEmployee) + { + return new Employee + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Remark = srcEmployee.Remark, + CreatedAt = srcEmployee.CreatedAt, + CreatedBy = srcEmployee.CreatedBy, + Identity = this.To(srcEmployee.Identity) + }; + } + + public Identity To(IdentityEntity srcIdentity) + { + return new Identity + { + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreatedAt = srcIdentity.CreateAt, + CreatedBy = srcIdentity.CreateBy, + }; + } + + public async Task SaveChangeAsync(IEmployeeAggregate srcEmployee, + CancellationToken cancel = default) + { + // var employeeEntity = srcEmployee.GetInstance(); + var employee = new Employee(); + foreach (var change in srcEmployee.GetChangeActions()) + { + change(employee); + } + + await using var dbContext = await this._employeeContextFactory.CreateDbContextAsync(cancel); + return 1; + } + + public async Task SaveChangeAsync(EmployeeAggregate srcEmployee, CancellationToken cancel = default) + { + await using var dbContext = await this._employeeContextFactory.CreateDbContextAsync(cancel); + + if (srcEmployee.State != EntityState.Submitted) + { + throw new Exception($"尚未 {nameof(EntityState.Submitted)},不能存檔"); + } + + var employeeFromDb = await dbContext.Employees + .FirstOrDefaultAsync(x => x.Id == srcEmployee.Id, cancel); + if (employeeFromDb == null) + { + var toDb = srcEmployee.GetInstance().ToDataEntity(); + dbContext.Add(toDb); + } + else + { + foreach (var changeAction in srcEmployee.GetChangeActions()) + { + changeAction(employeeFromDb); + } + } + + return await dbContext.SaveChangesAsync(cancel); + } + + public Task SaveChangeAsync(IEmployeeEntity employee, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public async Task GetAsync(Guid id, CancellationToken cancel = default) + { + await using var dbContext = await this._employeeContextFactory.CreateDbContextAsync(cancel); + return await dbContext.Employees + .Where(p => p.Id == id) + .AsNoTracking() + .FirstOrDefaultAsync(cancel); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs index 0564926a..4a2ae1fc 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -1,8 +1,13 @@ -using Lab.ChangeTracking.Domain.Entity; +using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Domain.Entity; namespace Lab.ChangeTracking.Domain.Repository; public interface IEmployeeRepository { - Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); + // Task SaveChangeAsync(IEmployeeAggregate employee, CancellationToken cancel = default); + Task SaveChangeAsync(EmployeeAggregate employee, CancellationToken cancel = default); + Task SaveChangeAsync(IEmployeeEntity employee, CancellationToken cancel = default); + + Task GetAsync(Guid id, CancellationToken cancel); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs new file mode 100644 index 00000000..9f7162be --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs @@ -0,0 +1,22 @@ +using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain.Repository; + +static class EmployeeRepositoryExtensions +{ + public static Employee ToDataEntity(this IEmployeeEntity srcEmployee) + { + return new Employee + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Remark = srcEmployee.Remark, + CreatedAt = srcEmployee.CreatedAt, + CreatedBy = srcEmployee.CreatedBy, + // Identity = this.To(srcEmployee.Identity) + }; + } + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs index 7b4c0e5c..7b9757ca 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EntityState.cs @@ -2,8 +2,8 @@ namespace Lab.ChangeTracking.Domain; public enum EntityState { - Added, - Unchanged, - Modified, - Submitted + Added = 0, + Modified = 1, + Submitted = 2, + Unchanged = 99, } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs new file mode 100644 index 00000000..6f41f399 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs @@ -0,0 +1,22 @@ +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Domain; + +public interface IAggregationRoot where T : IChangeTrackable +{ + IReadOnlyList> GetChangeActions(); + + void SetInstance(T instance); + + T GetInstance(); + + /// + /// SubmitChange 後則進版號 + /// + /// + /// + /// + (Error err, bool changed) SubmitChange(DateTimeOffset when, string who); + + void ChangeTrack(Action change); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/ISystemClock.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/ISystemClock.cs new file mode 100644 index 00000000..945faa76 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/ISystemClock.cs @@ -0,0 +1,11 @@ +namespace Lab.ChangeTracking.Domain; + +public interface ISystemClock +{ + DateTimeOffset Now { get; set; } +} + +public class SystemClock : ISystemClock +{ + public DateTimeOffset Now { get; set; }=DateTimeOffset.Now; +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IUUIDProvider.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IUUIDProvider.cs new file mode 100644 index 00000000..a58ce071 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IUUIDProvider.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IUUIdProvider +{ + Guid GenerateId(); +} + +public class UUIdProvider : IUUIdProvider +{ + public Guid GenerateId() + { + return Guid.NewGuid(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index 80e699a6..03c02b92 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -19,24 +19,26 @@ public class EmployeeDbContext : DbContext public EmployeeDbContext(DbContextOptions options) : base(options) { - if (s_migrated[0]) - { - return; - } - - lock (s_migrated) - { - if (s_migrated[0] == false) - { - var sqlOptions = options.FindExtension(); - if (sqlOptions != null) - { - this.Database.Migrate(); - } - - s_migrated[0] = true; - } - } + // 改用 CI 執行 Migrate + + // if (s_migrated[0]) + // { + // return; + // } + // + // lock (s_migrated) + // { + // if (s_migrated[0] == false) + // { + // var sqlOptions = options.FindExtension(); + // if (sqlOptions != null) + // { + // this.Database.Migrate(); + // } + // + // s_migrated[0] = true; + // } + // } } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -63,7 +65,7 @@ public void Configure(EntityTypeBuilder builder) } } - internal class IdentityConfiguration : IEntityTypeConfiguration + private class IdentityConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { @@ -85,5 +87,27 @@ public void Configure(EntityTypeBuilder builder) .IsClustered(); } } + + private class ProfileConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Profile"); + builder.HasKey(p => p.Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithMany(p => p.Profiles) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade) ; + + builder.Property(p => p.FirstName).IsRequired(); + builder.Property(p => p.LastName).IsRequired(); + builder.Property(p => p.Remark).IsRequired(false); + builder.Property(p => p.UpdatedBy).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs index 20ed9ff4..e5c4ebca 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs @@ -6,6 +6,10 @@ public class Profile : IProfileEntity { public Guid Id { get; set; } + public Guid Employee_Id { get; set; } + + public Employee Employee { get; set; } + public DateTimeOffset CreatedAt { get; set; } public string CreatedBy { get; set; } @@ -17,4 +21,8 @@ public class Profile : IProfileEntity public string FirstName { get; set; } public string LastName { get; set; } + + public string Remark { get; set; } + + public int SequenceId { get; set; } } \ No newline at end of file From 0573c8c944c1e4e6591864e9fb02f054137b5cc7 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 3 Mar 2022 08:20:10 +0800 Subject: [PATCH 166/267] refactor: add accessContext --- .../ChangeTrackingUnitTest.cs | 21 ++++++---- .../TestAssistants.cs | 4 +- .../AccessContext.cs | 8 ++++ .../AggregationRoot.cs | 37 ++++++---------- .../EmployeeAggregate/EmployeeAggregate.cs | 42 ++++++++----------- .../Entity/EmployeeEntity.cs | 2 +- .../Entity/IdentityEntity.cs | 2 +- .../EmployeeAggregate/Entity/ProfileEntity.cs | 2 +- .../EmployeeAggregate/IEmployeeAggregate.cs | 3 +- .../Repository/EmployeeRepository.cs | 16 +++---- .../Repository/IEmployeeRepository.cs | 3 +- .../EmployeeRepositoryExtensions.cs | 2 +- .../IAccessContext.cs | 8 ++++ .../IAggregationRoot.cs | 6 +-- 14 files changed, 79 insertions(+), 77 deletions(-) create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AccessContext.cs create mode 100644 Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAccessContext.cs diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 5b9e1835..15eb588f 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -5,6 +5,7 @@ using System.Text.Unicode; using System.Threading.Tasks; using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Domain.EmployeeAggregate; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Lab.MultiTestCase.UnitTest; using Microsoft.EntityFrameworkCore; @@ -30,11 +31,15 @@ public async Task 新增() systemClock.Now.Returns(DateTimeOffset.Parse("2021-01-01")); var uuIdProvider = Substitute.For(); uuIdProvider.GenerateId().Returns(TestAssistants.Parse("1")); + var accessContext = Substitute.For(); + accessContext.AccessNow.Returns(DateTimeOffset.Parse("2021-01-02")); + accessContext.AccessUserId.Returns("System User"); // act - var employeeAggregate = new EmployeeAggregate(employeeRepository, uuIdProvider, systemClock); - employeeAggregate.CreateInstance("yao", 18, "Test User"); - employeeAggregate.SubmitChange(systemClock.Now, "Sys1"); + var employeeAggregate = + new EmployeeAggregate.EmployeeAggregate(employeeRepository, uuIdProvider, systemClock, accessContext); + employeeAggregate.Initial("yao", 18, "Test User"); + employeeAggregate.SubmitChange(); var result = await employeeAggregate.SaveChangeAsync(); // assert @@ -56,13 +61,15 @@ public async Task 編輯() systemClock.Now.Returns(DateTimeOffset.Parse("2021-01-02")); var uuIdProvider = Substitute.For(); uuIdProvider.GenerateId().Returns(TestAssistants.Parse("1")); + var accessContext = Substitute.For(); + accessContext.AccessNow.Returns(DateTimeOffset.Parse("2021-01-02")); // act - var employeeAggregate = new EmployeeAggregate(employeeRepository, uuIdProvider, systemClock); + var employeeAggregate = + new EmployeeAggregate.EmployeeAggregate(employeeRepository, uuIdProvider, systemClock, accessContext); await employeeAggregate.GetAsync(TestAssistants.Parse("1")); - employeeAggregate.SetName("小章"); - employeeAggregate.SetAge(20); - employeeAggregate.SubmitChange(systemClock.Now, "Sys1"); + employeeAggregate.SetName("小章").SetAge(20); + employeeAggregate.SubmitChange(); var count = await employeeAggregate.SaveChangeAsync(); // assert diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs index dd355049..1c502ed8 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -1,7 +1,8 @@ using System; using Lab.ChangeTracking.Abstract; using Lab.ChangeTracking.Domain; -using Lab.ChangeTracking.Domain.Repository; +using Lab.ChangeTracking.Domain.EmployeeAggregate; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; using Lab.ChangeTracking.Infrastructure.DB; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Microsoft.EntityFrameworkCore; @@ -36,6 +37,7 @@ public static void ConfigureTestServices(IServiceCollection services) services.AddEntityFramework(); services.AddSingleton(); + services.AddSingleton(); // services.AddSingleton(); _serviceProvider = services.BuildServiceProvider(); } diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AccessContext.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AccessContext.cs new file mode 100644 index 00000000..fb42dfc6 --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AccessContext.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public class AccessContext : IAccessContext +{ + public string AccessUserId { get; set; } + + public DateTimeOffset AccessNow { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs index 6dc74137..6e115ba5 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/AggregationRoot.cs @@ -54,8 +54,10 @@ public int Version private readonly IList> _changeActions = new List>(); protected readonly Dictionary ChangedProperties = new(); protected readonly Dictionary OriginalValues = new(); - + protected IUUIdProvider _uuIdProvider; protected T _instance; + protected ISystemClock _systemClock; + protected IAccessContext _accessContext; public IReadOnlyList> GetChangeActions() { @@ -64,32 +66,17 @@ public IReadOnlyList> GetChangeActions() public void SetInstance(T instance) { - this.State = EntityState.Unchanged; this._instance = instance; - } - - public T GetInstance() - { - return this._instance; - - // return new T - // { - // Version = _instance.Version, - // CreatedAt = _instance.CreatedAt, - // CreatedBy = this._instance.CreatedBy, - // UpdatedAt = this.UpdatedAt, - // UpdatedBy = this.CreatedBy - // }; + this.State = EntityState.Unchanged; } /// /// SubmitChange 後則進版號 /// - /// - /// /// - public (Error err, bool changed) SubmitChange(DateTimeOffset when, string who) + public (Error err, bool changed) SubmitChange() { + var (now,accessUserId )= (this._accessContext.AccessNow,this._accessContext.AccessUserId); if (this.State == EntityState.Submitted) { return ( @@ -104,16 +91,16 @@ public T GetInstance() if (this.State == EntityState.Added) { - this.ChangeTrack(x => x.CreatedAt = when); - this.ChangeTrack(x => x.CreatedBy = who); - this.ChangeTrack(x => x.UpdatedAt = when); - this.ChangeTrack(x => x.UpdatedBy = who); + this.ChangeTrack(x => x.CreatedAt = now); + this.ChangeTrack(x => x.CreatedBy = accessUserId); + this.ChangeTrack(x => x.UpdatedAt = now); + this.ChangeTrack(x => x.UpdatedBy = accessUserId); this.ChangeTrack(x => x.Version += 1); } else { - this.ChangeTrack(x => x.UpdatedAt = when); - this.ChangeTrack(x => x.UpdatedBy = who); + this.ChangeTrack(x => x.UpdatedAt = now); + this.ChangeTrack(x => x.UpdatedBy = accessUserId); this.ChangeTrack(x => x.Version += 1); } diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs index 18a2e0a4..85d15e12 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -1,8 +1,8 @@ using Lab.ChangeTracking.Abstract; -using Lab.ChangeTracking.Domain.Entity; -using Lab.ChangeTracking.Domain.Repository; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; -namespace Lab.ChangeTracking.Domain; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate; // public class EmployeeAggregate : AggregationRoot, // IEmployeeAggregate @@ -17,36 +17,18 @@ public class EmployeeAggregate : AggregationRoot public string Remark => this._instance.Remark; private readonly IEmployeeRepository _repository; - private readonly ISystemClock _systemClock; - private readonly IUUIdProvider _uuIdProvider; - - private string _name; public EmployeeAggregate(IEmployeeRepository repository, IUUIdProvider uuIdProvider, - ISystemClock systemClock) + ISystemClock systemClock, + IAccessContext accessContext) { this._repository = repository; this._uuIdProvider = uuIdProvider; + this._accessContext = accessContext; this._systemClock = systemClock; } - public void CreateInstance(string name, int age, string remark = null) - { - var now = this._systemClock.Now; - var instance = new EmployeeEntity - { - Id = this._uuIdProvider.GenerateId(), - Name = name, - Age = age, - Version = 1, - Remark = remark, - Identity = null - }; - this.State = EntityState.Added; - this._instance = instance; - } - public async Task GetAsync(Guid id, CancellationToken cancel = default) { this._instance = await this._repository.GetAsync(id, cancel); @@ -54,6 +36,18 @@ public async Task GetAsync(Guid id, CancellationToken cancel = default) this.State = EntityState.Unchanged; } + public void Initial(string name, int age, string remark = null) + { + this._instance = new EmployeeEntity(); + + this.ChangeTrack(p => p.Id = this._uuIdProvider.GenerateId()); + this.ChangeTrack(p => p.Age = age); + this.ChangeTrack(p => p.Name = name); + this.ChangeTrack(p => p.Version = 0); + this.ChangeTrack(p => p.Remark = remark); + this.State = EntityState.Added; + } + public async Task SaveChangeAsync(CancellationToken cancel = default) { return await this._repository.SaveChangeAsync(this, cancel); diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index 197bb5c8..4b0f32ae 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -1,6 +1,6 @@ using Lab.ChangeTracking.Abstract; -namespace Lab.ChangeTracking.Domain.Entity; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; public record EmployeeEntity : IEmployeeEntity { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs index 457beb06..09505d0f 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -1,4 +1,4 @@ -namespace Lab.ChangeTracking.Domain.Entity; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; public record IdentityEntity { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs index 88550e4b..9ae35128 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs @@ -1,4 +1,4 @@ -namespace Lab.ChangeTracking.Domain.Entity; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; public record ProfileEntity { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs index 986572dc..c86d7c76 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -1,7 +1,6 @@ using Lab.ChangeTracking.Abstract; -using Lab.ChangeTracking.Domain.Entity; -namespace Lab.ChangeTracking.Domain; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate; public interface IEmployeeAggregate : IAggregationRoot where T : IChangeTrackable { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index c42e8103..29f3515a 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -1,12 +1,9 @@ -using ChangeTracking; -using EFCore.BulkExtensions; -using Lab.ChangeTracking.Abstract; -using Lab.ChangeTracking.Domain.Entity; -using Lab.ChangeTracking.Domain.Repository; +using Lab.ChangeTracking.Abstract; +using Lab.ChangeTracking.Domain.EmployeeAggregate.Entity; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Microsoft.EntityFrameworkCore; -namespace Lab.ChangeTracking.Domain.Repository; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; public class EmployeeRepository : IEmployeeRepository { @@ -69,7 +66,12 @@ public async Task SaveChangeAsync(EmployeeAggregate srcEmployee, Cancellati .FirstOrDefaultAsync(x => x.Id == srcEmployee.Id, cancel); if (employeeFromDb == null) { - var toDb = srcEmployee.GetInstance().ToDataEntity(); + var toDb = new Employee(); + foreach (var changeAction in srcEmployee.GetChangeActions()) + { + changeAction(toDb); + } + dbContext.Add(toDb); } else diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs index 4a2ae1fc..2ebb4645 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -1,7 +1,6 @@ using Lab.ChangeTracking.Abstract; -using Lab.ChangeTracking.Domain.Entity; -namespace Lab.ChangeTracking.Domain.Repository; +namespace Lab.ChangeTracking.Domain.EmployeeAggregate.Repository; public interface IEmployeeRepository { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs index 9f7162be..3810dc0e 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/EmployeeRepositoryExtensions.cs @@ -1,7 +1,7 @@ using Lab.ChangeTracking.Abstract; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; -namespace Lab.ChangeTracking.Domain.Repository; +namespace Lab.ChangeTracking.Domain; static class EmployeeRepositoryExtensions { diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAccessContext.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAccessContext.cs new file mode 100644 index 00000000..73dbed6c --- /dev/null +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAccessContext.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IAccessContext +{ + string AccessUserId { get; set; } + + DateTimeOffset AccessNow { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs index 6f41f399..2a5c4ade 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain/IAggregationRoot.cs @@ -8,15 +8,11 @@ public interface IAggregationRoot where T : IChangeTrackable void SetInstance(T instance); - T GetInstance(); - /// /// SubmitChange 後則進版號 /// - /// - /// /// - (Error err, bool changed) SubmitChange(DateTimeOffset when, string who); + (Error err, bool changed) SubmitChange(); void ChangeTrack(Action change); } \ No newline at end of file From e621dd242082358cbd43325f8458bad451559184 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 3 Mar 2022 09:01:20 +0800 Subject: [PATCH 167/267] add profile data --- .../ChangeTrackingUnitTest.cs | 65 +++++++++++++++++-- .../EntityModel/Employee.cs | 2 +- .../EntityModel/EmployeeDbContext.cs | 1 + .../EntityModel/Profile.cs | 4 +- 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 15eb588f..78689c66 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; @@ -79,26 +80,76 @@ public async Task 編輯() Assert.AreEqual(20, actual.Age); Assert.AreEqual(2, actual.Version); } + [TestMethod] + public async Task 編輯1() + { + // arrange + InsertTestData(); + await using var dbContext = await TestAssistants.EmployeeDbContextFactory.CreateDbContextAsync(); + + var employee = dbContext.Employees + .Include(p => p.Profiles) + .AsTracking() + // .Load() + .FirstOrDefault() + ; + var now = DateTimeOffset.Now; + var accessUserId = "TEST USER"; + var newProfile = new Profile + { + Id = Guid.NewGuid(), + Employee_Id = employee.Id, + CreatedAt = now, + CreatedBy = accessUserId, + UpdatedAt = now, + UpdatedBy = accessUserId, + FirstName = "first name", + LastName = "last name", + }; + employee.Profiles.Add(newProfile); + // dbContext.Profiles.Add(newProfile); + await dbContext.SaveChangesAsync(); + } private static Employee InsertTestData() { Console.WriteLine("新增資料"); using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var employeeId = TestAssistants.Parse("1"); + var now = DateTimeOffset.Now; + var accessUserId = "TEST USER"; var toDB = new Employee { - Id = TestAssistants.Parse("1"), + Id = employeeId, Age = 18, Name = "yao", - CreatedAt = DateTimeOffset.Now, - CreatedBy = "TEST", - Identity = new Identity + CreatedAt = now, + CreatedBy = accessUserId, + Identity = new() { + Employee_Id = employeeId, Account = "yao", Password = "123456", - CreatedAt = DateTimeOffset.Now, - CreatedBy = "TEST", + CreatedAt = now, + CreatedBy = accessUserId, + UpdatedAt = now, + UpdatedBy = accessUserId, + }, + Version = 1, + Profiles = new List() + { + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = now, + CreatedBy = accessUserId, + UpdatedAt = now, + UpdatedBy = accessUserId, + FirstName = "yao", + LastName = "yu", + } }, - Version = 1 }; dbContext.Employees.Add(toDB); dbContext.SaveChanges(); diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs index 42cac8ca..49094f01 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -15,7 +15,7 @@ public class Employee : IEmployeeEntity public string Remark { get; set; } - public IList Profiles { get; set; } + public List Profiles { get; set; } = new(); public Identity Identity { get; set; } diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index 03c02b92..03ec4140 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -45,6 +45,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); modelBuilder.ApplyConfiguration(new IdentityConfiguration()); + modelBuilder.ApplyConfiguration(new ProfileConfiguration()); } internal class EmployeeConfiguration : IEntityTypeConfiguration diff --git a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs index e5c4ebca..140c8f05 100644 --- a/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs +++ b/Property Change Tracking/ChangeTrackingForORM/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Profile.cs @@ -1,4 +1,5 @@ -using Lab.ChangeTracking.Abstract; +using System.ComponentModel.DataAnnotations.Schema; +using Lab.ChangeTracking.Abstract; namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; @@ -24,5 +25,6 @@ public class Profile : IProfileEntity public string Remark { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int SequenceId { get; set; } } \ No newline at end of file From 3ca9bd0ff24386404e357a743874577b2a49ec79 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 5 Mar 2022 17:16:02 +0800 Subject: [PATCH 168/267] =?UTF-8?q?feat:=20=E5=AF=A6=E4=BD=9C=20change=20t?= =?UTF-8?q?rack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackProperty.sln | 41 +++++ .../ChangeTrackProperty/Makefile | 4 + .../ChangeTrackProperty/docker-compose.yml | 10 ++ .../ChangeTrackingUnitTest.cs | 81 ++++++++++ .../Lab.ChangeTracking.Domain.UnitTest.csproj | 28 ++++ .../MsTestHook.cs | 31 ++++ .../TestAssistants.cs | 57 +++++++ .../AccessContext.cs | 14 ++ .../EmployeeAggregate/EmployeeAggregate.cs | 29 ++++ .../Entity/EmployeeEntity.cs | 55 +++++++ .../Entity/IdentityEntity.cs | 14 ++ .../EmployeeAggregate/Entity/ProfileEntity.cs | 8 + .../EmployeeAggregate/IEmployeeAggregate.cs | 6 + .../Repository/EmployeeRepository.cs | 46 ++++++ .../Repository/IEmployeeRepository.cs | 6 + .../Lab.ChangeTracking.Domain/EntityBase.cs | 144 ++++++++++++++++++ .../Lab.ChangeTracking.Domain/EntityState.cs | 9 ++ .../src/Lab.ChangeTracking.Domain/Error.cs | 3 + .../Lab.ChangeTracking.Domain/IChangeTime.cs | 32 ++++ .../Lab.ChangeTracking.Domain/IChangeable.cs | 6 + .../Lab.ChangeTracking.Domain/ISystemClock.cs | 14 ++ .../IUUIdProvider.cs | 14 ++ .../Lab.ChangeTracking.Domain.csproj | 16 ++ .../PropertyChangeTracker.cs | 48 ++++++ .../src/Lab.ChangeTracking.Domain/Survey.cs | 39 +++++ .../AppDependencyInjectionExtensions.cs | 46 ++++++ .../AppEnvironmentOption.cs | 32 ++++ .../EntityModel/Employee.cs | 30 ++++ .../EntityModel/EmployeeDbContext.cs | 76 +++++++++ .../EntityModel/Identity.cs | 32 ++++ .../EntityModel/OrderHistory.cs | 29 ++++ .../EnvironmentAssistant.cs | 15 ++ ...ab.ChangeTracking.Infrastructure.DB.csproj | 15 ++ 33 files changed, 1030 insertions(+) create mode 100644 Property Change Tracking/ChangeTrackProperty/ChangeTrackProperty.sln create mode 100644 Property Change Tracking/ChangeTrackProperty/Makefile create mode 100644 Property Change Tracking/ChangeTrackProperty/docker-compose.yml create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/AccessContext.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Error.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/ISystemClock.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj diff --git a/Property Change Tracking/ChangeTrackProperty/ChangeTrackProperty.sln b/Property Change Tracking/ChangeTrackProperty/ChangeTrackProperty.sln new file mode 100644 index 00000000..1c868fe3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/ChangeTrackProperty.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Infrastructure.DB", "src\Lab.ChangeTracking.Infrastructure.DB\Lab.ChangeTracking.Infrastructure.DB.csproj", "{A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain", "src\Lab.ChangeTracking.Domain\Lab.ChangeTracking.Domain.csproj", "{A1C827F2-683E-470C-A3DE-BD6DD2BE5198}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain.UnitTest", "src\Lab.ChangeTracking.Domain.UnitTest\Lab.ChangeTracking.Domain.UnitTest.csproj", "{7066EE2C-28A8-4408-B212-E582608AB967}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.Build.0 = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.Build.0 = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {7066EE2C-28A8-4408-B212-E582608AB967} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Property Change Tracking/ChangeTrackProperty/Makefile b/Property Change Tracking/ChangeTrackProperty/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/docker-compose.yml b/Property Change Tracking/ChangeTrackProperty/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs new file mode 100644 index 00000000..8a7736ca --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -0,0 +1,81 @@ +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Lab.MultiTestCase.UnitTest; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Employee = Lab.ChangeTracking.Infrastructure.DB.EntityModel.Employee; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class ChangeTrackingUnitTest +{ + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + + private readonly IDbContextFactory _employeeDbContextFactory = + TestAssistants.EmployeeDbContextFactory; + + private ISystemClock _systemClock = TestAssistants.SystemClock; + + public static IAccessContext _accessContext = TestAssistants.AccessContext; + + public static IUUIdProvider _uuIdProvider = TestAssistants.UUIdProvider; + + [TestMethod] + public void 追蹤() + { + var employeeEntity = new EmployeeEntity + { + Id = Guid.NewGuid(), + State = EntityState.Added, + Name = "yao", + Age = 19, + Remark = "remark" + }; + employeeEntity.InitialTrack(); + employeeEntity.SetProfile("小章", 20,"remark"); + employeeEntity.SetProfile("小章", 20,"remark"); + } + + [TestMethod] + public void 追蹤集合() + { + } + + private static Employee Insert() + { + using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var toDB = new Employee + { + Id = Guid.NewGuid(), + Age = 18, + Name = "yao", + CreateAt = DateTimeOffset.Now, + CreateBy = "TEST", + Identity = new Identity + { + Account = "yao", + Password = "123456", + CreateAt = DateTimeOffset.Now, + CreateBy = "TEST" + } + }; + dbContext.Employees.Add(toDB); + dbContext.SaveChanges(); + return toDB; + } + + private static string ToJson(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj new file mode 100644 index 00000000..fbfda1f2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..ca75b47b --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MultiTestCase.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs new file mode 100644 index 00000000..af3c81ad --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -0,0 +1,57 @@ +using System; +using Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Infrastructure.DB; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.MultiTestCase.UnitTest; + +// assistant +internal class TestAssistants +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + public static IEmployeeRepository EmployeeRepository => + _serviceProvider.GetService(); + + public static IEmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService(); + + public static ISystemClock SystemClock => + _serviceProvider.GetService(); + + public static IAccessContext AccessContext => + _serviceProvider.GetService(); + + public static IUUIdProvider UUIdProvider => + _serviceProvider.GetService(); + + static TestAssistants() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + SetTestEnvironmentVariable(); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/AccessContext.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/AccessContext.cs new file mode 100644 index 00000000..85ba24fb --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/AccessContext.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IAccessContext +{ + public string? GetUserId(); +} + +public class AccessContext : IAccessContext +{ + public string? GetUserId() + { + return "Sys"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs new file mode 100644 index 00000000..231e5bbc --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -0,0 +1,29 @@ +namespace Lab.ChangeTracking.Domain; + +public class EmployeeAggregate +{ + private IEmployeeRepository _repository; + private IUUIdProvider _idProvider; + private ISystemClock _systemClock; + private IAccessContext _accessContext; + private EmployeeEntity _employeeEntity; + + public void Create() + { + } + public EmployeeAggregate(IEmployeeRepository repository, + IUUIdProvider idProvider, + ISystemClock systemClock, + IAccessContext accessContext) + { + this._repository = repository; + this._idProvider = idProvider; + this._systemClock = systemClock; + this._accessContext = accessContext; + } + + void Save() + { + + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs new file mode 100644 index 00000000..ebb87622 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -0,0 +1,55 @@ +namespace Lab.ChangeTracking.Domain; + +public record EmployeeEntity : EntityBase +{ + public string Name + { + get => this._name; + init => this._name = value; + } + + public int? Age + { + get => this._age; + init => this._age = value; + } + + public string Remark + { + get => this._remark; + init => this._remark = value; + } + + private IAccessContext _accessContext; + private int? _age; + + // public IList Profiles { get; private set; } = new(); + // + // public IdentityEntity Identity { get; private set; } = new(); + + private IUUIdProvider _idProvider; + private string _name; + private string _remark; + private ISystemClock _systemClock; + + public EmployeeEntity SetId(string name, int age, string remark = null) + { + this._name = name; + this._age = age; + this._remark = remark; + this.ChangeTrack(nameof(this.Name), name); + this.ChangeTrack(nameof(this.Age), age); + this.ChangeTrack(nameof(this.Remark), remark); + return this; + } + public EmployeeEntity SetProfile(string name, int age, string remark = null) + { + this._name = name; + this._age = age; + this._remark = remark; + this.ChangeTrack(nameof(this.Name), name); + this.ChangeTrack(nameof(this.Age), age); + this.ChangeTrack(nameof(this.Remark), remark); + return this; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs new file mode 100644 index 00000000..1435576f --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public record IdentityEntity +{ + public virtual string Account { get; set; } + + public virtual string Password { get; set; } + + public virtual string Remark { get; set; } + + public virtual DateTimeOffset CreateAt { get; set; } + + public virtual string CreateBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs new file mode 100644 index 00000000..8ab5d414 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public record ProfileEntity +{ + public virtual string FirstName { get; set; } + + public virtual string LastName { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..224c9362 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeAggregate +{ + Task ModifyFlowAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs new file mode 100644 index 00000000..97f22e66 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -0,0 +1,46 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeRepository : IEmployeeRepository +{ + private readonly IDbContextFactory _memberContextFactory; + + public EmployeeRepository(IDbContextFactory memberContextFactory) + { + this._memberContextFactory = memberContextFactory; + } + + public Infrastructure.DB.EntityModel.Employee To(EmployeeEntity srcEmployee) + { + return new Infrastructure.DB.EntityModel.Employee + { + // Id = srcEmployee.Id, + // Name = srcEmployee.Name, + // Age = srcEmployee.Age, + // Remark = srcEmployee.Remark, + // CreateAt = srcEmployee.CreatedAt + + // CreateBy = srcEmployee.CreatedBy, + // Identity = this.To(srcEmployee.Identity) + }; + } + + public Identity To(IdentityEntity srcIdentity) + { + return new Identity + { + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreateAt = srcIdentity.CreateAt, + CreateBy = srcIdentity.CreateBy + }; + } + + public async Task SaveChangeAsync(EmployeeEntity srcEmployee, + CancellationToken cancel = default) + { + throw new Exception(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..8748817c --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeRepository +{ + Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs new file mode 100644 index 00000000..7e675b00 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs @@ -0,0 +1,144 @@ +namespace Lab.ChangeTracking.Domain; + +public record EntityBase : IChangeTrackable +{ + public Guid? Id + { + get => this._id; + init => this._id = value; + } + + private readonly Dictionary _changedProperties = new(); + private readonly Dictionary _originalValues = new(); + private DateTimeOffset? _createdAt; + private string? _createdBy; + private Guid? _id; + private DateTimeOffset? _modifiedAt; + private string? _modifiedBy; + private EntityState _state; + private int _version; + + public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, + IAccessContext accessContext, + IUUIdProvider idProvider) + { + if (this.State == EntityState.Submitted) + { + return ( + new Error("STATE_CONFLICT", + $"Entity({this.State}) was submitted and should not submit again."), false); + } + + var (now, accessUserId) = (systemClock.GetNow(), accessContext.GetUserId()); + + if (this.State == EntityState.Unchanged) + { + return (null, false); + } + + if (this.State == EntityState.Added) + { + this._id = idProvider.GenerateId(); + this._createdAt = now; + this._createdBy = accessUserId; + this._version = 1; + } + else + { + this._version = this._version++; + } + + this._modifiedAt = now; + this._modifiedBy = accessUserId; + + this._state = EntityState.Submitted; + + return (null, true); + } + + public void InitialTrack() + { + this._state = EntityState.Added; + this._version = 1; + var properties = this.GetType().GetProperties(); + foreach (var property in properties) + { + this._originalValues.Add(property.Name, property.GetValue(this)); + } + } + + public Dictionary GetChangedProperties() + { + return this._changedProperties; + } + + public bool HasChanged { get; private set; } + + public EntityState State + { + get => this._state; + init => this._state = value; + } + + public int Version + { + get => this._version; + init => this._version = value; + } + + public Dictionary GetOriginalValues() + { + return this._originalValues; + } + + protected void ChangeTrack(string propertyName, object value) + { + if (this.State == EntityState.Submitted) + { + throw new Exception("已經 Submitted ,無法再進行修改。"); + } + + var changes = this._changedProperties; + var originals = this._originalValues; + if (changes.ContainsKey(propertyName) == false) + { + if (originals[propertyName] != value) + { + changes.Add(propertyName, value); + this._state = EntityState.Added; + } + } + else + { + if (originals[propertyName] != changes[propertyName]) + { + changes[propertyName] = value; + this._state = EntityState.Modified; + } + } + } + + public DateTimeOffset? CreatedAt + { + get => this._createdAt; + init => this._createdAt = value; + } + + public string? CreatedBy + { + get => this._createdBy; + init => this._createdBy = value; + } + + public DateTimeOffset? ModifiedAt + { + get => this._modifiedAt; + init => this._modifiedAt = value; + } + + public string? ModifiedBy + { + get => this._modifiedBy; + init => this._modifiedBy = value; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs new file mode 100644 index 00000000..2573f480 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs @@ -0,0 +1,9 @@ +namespace Lab.ChangeTracking.Domain; + +public enum EntityState +{ + Unchanged = 0, + Added = 1, + Modified = 2, + Submitted = 99, +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Error.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Error.cs new file mode 100644 index 00000000..aa8f1b7a --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Error.cs @@ -0,0 +1,3 @@ +namespace Lab.ChangeTracking.Domain; + +public record Error(T Code, object Message); \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs new file mode 100644 index 00000000..c16ef976 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs @@ -0,0 +1,32 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeTime +{ + DateTimeOffset? CreatedAt { get; init; } + + string? CreatedBy { get; init; } + + DateTimeOffset? ModifiedAt { get; init; } + + string? ModifiedBy { get; init; } +} + +public interface IChangeState +{ + EntityState State { get; init; } + + int Version { get; init; } +} + +public interface IChangeTrackable : IChangeTime, IChangeState +{ + bool HasChanged { get; } + + Dictionary GetChangedProperties(); + + Dictionary GetOriginalValues(); + + // void SetTrackable(); + // + // void ChangeTrack(string propertyName, object value); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs new file mode 100644 index 00000000..2c5125f8 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeable:IChangeTrackable +{ + +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/ISystemClock.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/ISystemClock.cs new file mode 100644 index 00000000..50076e0d --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/ISystemClock.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface ISystemClock +{ + DateTimeOffset GetNow(); +} + +public class SystemClock : ISystemClock +{ + public DateTimeOffset GetNow() + { + return DateTimeOffset.Now; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs new file mode 100644 index 00000000..a58ce071 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IUUIdProvider +{ + Guid GenerateId(); +} + +public class UUIdProvider : IUUIdProvider +{ + public Guid GenerateId() + { + return Guid.NewGuid(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj new file mode 100644 index 00000000..a7984ff8 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs new file mode 100644 index 00000000..26cfefd8 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs @@ -0,0 +1,48 @@ +namespace Lab.ChangeTracking.Domain; + +public class PropertyChangeTracker +{ + private readonly Dictionary _changedProperties = new(); + private readonly Dictionary _originalValues = new(); + + public Dictionary GetChangedProperties() + { + return this._changedProperties; + } + + public Dictionary GetOriginalValues() + { + throw new NotImplementedException(); + } + + public void Initial() + { + var properties = this.GetType().GetProperties(); + foreach (var property in properties) + { + this._originalValues.Add(property.Name, property.GetValue(this)); + } + } + + public void Track(string propertyName, object value) + { + var changes = this._changedProperties; + var originals = this._originalValues; + if (changes.ContainsKey(propertyName) == false) + { + changes.Add(propertyName, value); + } + else + { + if (originals[propertyName] != changes[propertyName]) + { + changes[propertyName] = value; + } + + if (originals[propertyName] == changes[propertyName]) + { + changes.Remove(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs new file mode 100644 index 00000000..9a0f3d26 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; +using System.Reflection; + +namespace Lab.ChangeTracking.Domain; + +public class Base : IRevertibleChangeTracking +{ + protected readonly Dictionary ChangedProperties = new(); + protected readonly Dictionary OriginalValues = new(); + + public void Initialize() + { + var properties = this.GetType().GetProperties(); + + // Save the current value of the properties to our dictionary. + foreach (var property in properties) + { + this.OriginalValues.Add(property.Name, property.GetValue(this)); + } + } + + public bool IsChanged { get; private set; } + + public void RejectChanges() + { + foreach (var property in this.ChangedProperties) + { + this.GetType().GetRuntimeProperty(property.Key).SetValue(this, property.Value); + } + + this.AcceptChanges(); + } + + public void AcceptChanges() + { + this.ChangedProperties.Clear(); + this.IsChanged = false; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..a76bc397 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,46 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ChangeTracking.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => { builder.AddConsole(); }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..d93a85cc --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,32 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + private string _employeeDbConnectionString; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..2e37227a --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +[Table("Employee")] +public class Employee +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + public virtual Identity Identity { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..f859963d --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,76 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +public class EmployeeDbContext : DbContext +{ + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identities { get; set; } + + public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + if (s_migrated[0]) + { + return; + } + + lock (s_migrated) + { + if (s_migrated[0] == false) + { + var memoryOptions = options.FindExtension(); + + if (memoryOptions == null) + { + var sqlOptions = options.FindExtension(); + if (sqlOptions != null) + { + this.Database.Migrate(); + } + } + + s_migrated[0] = true; + } + } + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.HasKey(e => e.Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + + modelBuilder.Entity(p => + { + p.HasKey(e => e.Employee_Id) + .IsClustered(false); + + p.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + + p.Property(p => p.Remark) + .IsRequired(false) + ; + }); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..f2251c34 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +[Table("Identity")] +public class Identity +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..6632c4e0 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +[Table("OrderHistory")] +public class OrderHistory +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..b115d8a2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj new file mode 100644 index 00000000..b1b261bb --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + From 62be34a93aaaf5d4c7fa53c9eadabca424173838 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 6 Mar 2022 01:10:28 +0800 Subject: [PATCH 169/267] =?UTF-8?q?feat:=20=E7=8B=80=E6=85=8B=E8=BF=BD?= =?UTF-8?q?=E8=B9=A4=E5=A5=97=E7=94=A8=20EF=20Core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTracking2/ChangeTracking2.sln | 48 ++++++ .../ChangeTracking2/Makefile | 4 + .../ChangeTracking2/docker-compose.yml | 10 ++ .../Lab.ChangeTracking.Abstract/IEntity.cs | 6 + .../Lab.ChangeTracking.Abstract.csproj | 9 + .../ChangeTrackingUnitTest.cs | 154 ++++++++++++++++++ .../Lab.ChangeTracking.Domain.UnitTest.csproj | 28 ++++ .../MsTestHook.cs | 31 ++++ .../TestAssistants.cs | 46 ++++++ .../Employee/Aggregate/EmployeeAggregate.cs | 25 +++ .../Employee/Aggregate/IEmployeeAggregate.cs | 6 + .../Employee/Entity/AddressEntity.cs | 24 +++ .../Employee/Entity/EmployeeEntity.cs | 26 +++ .../Employee/Entity/IdentityEntity.cs | 20 +++ .../Employee/Repository/EmployeeRepository.cs | 30 ++++ .../Repository/IEmployeeRepository.cs | 6 + .../Employee/Repository/RepositoryBase.cs | 72 ++++++++ .../Repository/TypeConverterExtensions.cs | 138 ++++++++++++++++ .../Lab.ChangeTracking.Domain.csproj | 20 +++ .../AppDependencyInjectionExtensions.cs | 46 ++++++ .../AppEnvironmentOption.cs | 32 ++++ .../EntityModel/Address.cs | 30 ++++ .../EntityModel/Employee.cs | 35 ++++ .../EntityModel/EmployeeDbContext.cs | 120 ++++++++++++++ .../EntityModel/Identity.cs | 32 ++++ .../EnvironmentAssistant.cs | 15 ++ ...ab.ChangeTracking.Infrastructure.DB.csproj | 18 ++ 27 files changed, 1031 insertions(+) create mode 100644 Property Change Tracking/ChangeTracking2/ChangeTracking2.sln create mode 100644 Property Change Tracking/ChangeTracking2/Makefile create mode 100644 Property Change Tracking/ChangeTracking2/docker-compose.yml create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/EmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/IEmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/IEmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/TypeConverterExtensions.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj diff --git a/Property Change Tracking/ChangeTracking2/ChangeTracking2.sln b/Property Change Tracking/ChangeTracking2/ChangeTracking2.sln new file mode 100644 index 00000000..030aecc7 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/ChangeTracking2.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Infrastructure.DB", "src\Lab.ChangeTracking.Infrastructure.DB\Lab.ChangeTracking.Infrastructure.DB.csproj", "{A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain", "src\Lab.ChangeTracking.Domain\Lab.ChangeTracking.Domain.csproj", "{A1C827F2-683E-470C-A3DE-BD6DD2BE5198}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain.UnitTest", "src\Lab.ChangeTracking.Domain.UnitTest\Lab.ChangeTracking.Domain.UnitTest.csproj", "{7066EE2C-28A8-4408-B212-E582608AB967}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Abstract", "src\Lab.ChangeTracking.Abstract\Lab.ChangeTracking.Abstract.csproj", "{43C40083-77B5-4068-A707-1993D3B29410}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.Build.0 = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.Build.0 = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.Build.0 = Release|Any CPU + {43C40083-77B5-4068-A707-1993D3B29410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43C40083-77B5-4068-A707-1993D3B29410}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43C40083-77B5-4068-A707-1993D3B29410}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43C40083-77B5-4068-A707-1993D3B29410}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {7066EE2C-28A8-4408-B212-E582608AB967} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {43C40083-77B5-4068-A707-1993D3B29410} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Property Change Tracking/ChangeTracking2/Makefile b/Property Change Tracking/ChangeTracking2/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/docker-compose.yml b/Property Change Tracking/ChangeTracking2/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs new file mode 100644 index 00000000..a861cb96 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Abstract; + +public interface IEntity +{ + Guid Id { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/Lab.ChangeTracking.Abstract.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs new file mode 100644 index 00000000..ffac8aec --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using ChangeTracking; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class ChangeTrackingUnitTest +{ + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + + private readonly IDbContextFactory _employeeDbContextFactory = + TestAssistants.EmployeeDbContextFactory; + + private readonly IEmployeeRepository _employeeRepository = TestAssistants.EmployeeRepository; + + [TestMethod] + public void 異動追蹤後存檔() + { + var toDB = Insert().To(); + var trackable = toDB.AsTrackable(); + trackable.Age = 20; + trackable.Name = "小章"; + trackable.Identity.Remark = "我變了"; + trackable.Addresses[0].Remark = "我變了"; + trackable.Addresses.RemoveAt(1); + trackable.Addresses.Add(new AddressEntity() + { + Id = Guid.NewGuid(), + Employee_Id = toDB.Id, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "我新的" + }); + var employeeEntity = this._employeeRepository.SaveChangeAsync(trackable).Result; + } + + [TestMethod] + public void 異動追蹤後存檔_回傳不可變的物件() + { + var toDB = Insert(); + var source = new EmployeeEntity + { + Id = toDB.Id, + Name = "yao", + Age = 12, + Identity = new IdentityEntity + { + Employee_Id = toDB.Identity.Employee_Id + }, + Addresses = new List + { + new() + { + Id = toDB.Addresses[0] + .Id, + Employee_Id = toDB.Id, + Remark = "AAA" + }, + new() + { + Id = toDB.Addresses[1] + .Id, + Employee_Id = toDB.Id, + Remark = "AAA" + } + } + }; + var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; + this.DataShouldOk(source); + } + + private void DataShouldOk(EmployeeEntity source) + { + var dbContext = this._employeeDbContextFactory.CreateDbContext(); + var actual = dbContext.Employees + .Where(p => p.Id == source.Id) + .Include(p => p.Identity) + .First() + ; + + Assert.AreEqual("小章", actual.Name); + Assert.AreEqual("9527", actual.Identity.Password); + } + + private static Employee Insert() + { + using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var employeeId = Guid.NewGuid(); + var toDB = new Employee + { + Id = employeeId, + Age = 18, + Name = "yao", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", + Identity = new Identity + { + Employee_Id = employeeId, + Account = "yao", + Password = "123456", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", + Remark = "編輯" + }, + Addresses = new List
+ { + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "修改的" + }, + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "刪除的" + } + } + }; + dbContext.Employees.Add(toDB); + dbContext.SaveChanges(); + return toDB; + } + + private static string ToJson(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj new file mode 100644 index 00000000..fbfda1f2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..817754fe --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs new file mode 100644 index 00000000..d9741d2a --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -0,0 +1,46 @@ +using System; +using Lab.ChangeTracking.Infrastructure.DB; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +// assistant +internal class TestAssistants +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + public static IEmployeeRepository EmployeeRepository => + _serviceProvider.GetService(); + + public static IEmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService(); + + static TestAssistants() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + SetTestEnvironmentVariable(); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + + services.AddSingleton(); + services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/EmployeeAggregate.cs new file mode 100644 index 00000000..875cfc6c --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/EmployeeAggregate.cs @@ -0,0 +1,25 @@ +using ChangeTracking; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeAggregate : IEmployeeAggregate +{ + private readonly IEmployeeRepository _repository; + + public EmployeeAggregate(IEmployeeRepository repository) + { + this._repository = repository; + } + + public async Task ModifyFlowAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) + { + var memberTrackable = srcEmployee.AsTrackable(); + + memberTrackable.Remark = "我變了"; + memberTrackable.Identity.Remark = "我變了"; + memberTrackable.Addresses[0].Remark = "我變了"; + memberTrackable.Addresses.RemoveAt(1); + var changeCount = await this._repository.SaveChangeAsync(memberTrackable, cancel); + return memberTrackable; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..224c9362 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Aggregate/IEmployeeAggregate.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeAggregate +{ + Task ModifyFlowAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs new file mode 100644 index 00000000..67249ce5 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs @@ -0,0 +1,24 @@ +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Domain; + +public record AddressEntity:IEntity +{ + public virtual Guid Id { get; set; } + + public virtual Guid Employee_Id { get; set; } + + public virtual string Country { get; set; } + + public virtual string Street { get; set; } + + public virtual DateTimeOffset CreatedAt { get; set; } + + public virtual string CreatedBy { get; set; } + + public virtual DateTimeOffset? ModifiedAt { get; set; } + + public virtual string ModifiedBy { get; set; } + + public virtual string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs new file mode 100644 index 00000000..5bd37ec2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs @@ -0,0 +1,26 @@ +namespace Lab.ChangeTracking.Domain; + +public record EmployeeEntity +{ + public virtual Guid Id { get; set; } + + public virtual string Name { get; set; } + + public virtual int? Age { get; set; } + + public virtual int Version { get; set; } + + public virtual IdentityEntity Identity { get; set; } + + public virtual IList Addresses { get; set; } + + public virtual DateTimeOffset CreatedAt { get; set; } + + public virtual string CreatedBy { get; set; } + + public virtual DateTimeOffset? ModifiedAt { get; set; } + + public virtual string ModifiedBy { get; set; } + + public virtual string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs new file mode 100644 index 00000000..96092ab9 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs @@ -0,0 +1,20 @@ +namespace Lab.ChangeTracking.Domain; + +public record IdentityEntity +{ + public virtual Guid Employee_Id { get; set; } + + public virtual string Account { get; set; } + + public virtual string Password { get; set; } + + public virtual DateTimeOffset CreatedAt { get; set; } + + public virtual string CreatedBy { get; set; } + + public virtual DateTimeOffset? ModifiedAt { get; set; } + + public virtual string? ModifiedBy { get; set; } + + public virtual string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs new file mode 100644 index 00000000..712b70fc --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs @@ -0,0 +1,30 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeRepository : RepositoryBase, IEmployeeRepository +{ + private readonly IDbContextFactory _memberContextFactory; + + public EmployeeRepository(IDbContextFactory memberContextFactory) + { + this._memberContextFactory = memberContextFactory; + } + + public async Task SaveChangeAsync(EmployeeEntity srcEmployee, + CancellationToken cancel = default) + { + await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); + var destEmployee = srcEmployee.To(); + this.ApplyChange(dbContext, srcEmployee, destEmployee, new List + { + "Identity", + "Addresses" + } + ); + this.ApplyChange(dbContext, srcEmployee.Identity, destEmployee.Identity); + this.ApplyChanges(dbContext, srcEmployee.Addresses, destEmployee.Addresses); + return await dbContext.SaveChangesAsync(cancel); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..8748817c --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/IEmployeeRepository.cs @@ -0,0 +1,6 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeRepository +{ + Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs new file mode 100644 index 00000000..bb60a73e --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs @@ -0,0 +1,72 @@ +using ChangeTracking; +using Lab.ChangeTracking.Abstract; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain; + +public class RepositoryBase +{ + protected void ApplyChange(DbContext dbContext, + TSource source, + TTarget target, + IEnumerable excludeProperties = null) + where TSource : class + where TTarget : class + { + var sourceTrackable = source.CastToIChangeTrackable(); + var changedProperties = sourceTrackable.ChangedProperties; + dbContext.Set().Attach(target); + foreach (var property in changedProperties) + { + if (excludeProperties != null + && excludeProperties.Any(p => p == property)) + { + continue; + } + + dbContext.Entry(target).Property(property).IsModified = true; + } + } + + protected void ApplyChanges(DbContext dbContext, + IList sources, + IList targets, + IEnumerable excludeProperties = null) + where TSource : class, IEntity + where TTarget : class, IEntity + + { + var targetsTrackable = sources.CastToIChangeTrackableCollection(); + if (targetsTrackable == null) + { + return; + } + + var addedItems = targetsTrackable.AddedItems; + var deletedItems = targetsTrackable.DeletedItems; + foreach (var source in targetsTrackable.ChangedItems) + { + var target = targets.FirstOrDefault(p => p.Id == source.Id); + if (target == null) + { + continue; + } + + this.ApplyChange(dbContext, source, target, excludeProperties); + } + + foreach (var addedItem in addedItems) + { + + // dbContext.Add(addedItem as TTarget); + } + + foreach (var source in deletedItems) + { + var target = Activator.CreateInstance(); + target.Id = source.Id; + dbContext.Set().Attach(target); + dbContext.Entry(target).State = EntityState.Deleted; + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/TypeConverterExtensions.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/TypeConverterExtensions.cs new file mode 100644 index 00000000..8e8ae5d7 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/TypeConverterExtensions.cs @@ -0,0 +1,138 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain; + +public static class TypeConverterExtensions +{ + public static Employee To(this EmployeeEntity srcEmployee) + { + if (srcEmployee == null) + { + return null; + } + + return new Employee + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Version = srcEmployee.Version, + Remark = srcEmployee.Remark, + Addresses = srcEmployee.Addresses.To()?.ToList(), + Identity = srcEmployee.Identity.To(), + CreatedAt = srcEmployee.CreatedAt, + CreatedBy = srcEmployee.CreatedBy, + ModifiedAt = srcEmployee.ModifiedAt, + ModifiedBy = srcEmployee.ModifiedBy + }; + } + public static EmployeeEntity To(this Employee srcEmployee) + { + if (srcEmployee == null) + { + return null; + } + + return new EmployeeEntity + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Version = srcEmployee.Version, + Remark = srcEmployee.Remark, + Addresses = srcEmployee.Addresses.To()?.ToList(), + Identity = srcEmployee.Identity.To(), + CreatedAt = srcEmployee.CreatedAt, + CreatedBy = srcEmployee.CreatedBy, + ModifiedAt = srcEmployee.ModifiedAt, + ModifiedBy = srcEmployee.ModifiedBy + }; + } + public static Identity To(this IdentityEntity srcIdentity) + { + if (srcIdentity == null) + { + return null; + } + + return new Identity + { + Employee_Id = srcIdentity.Employee_Id, + Account = srcIdentity.Account, + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreatedAt = srcIdentity.CreatedAt, + CreatedBy = srcIdentity.CreatedBy, + ModifiedAt = srcIdentity.ModifiedAt, + ModifiedBy = srcIdentity.ModifiedBy + }; + } + public static IdentityEntity To(this Identity srcIdentity) + { + if (srcIdentity == null) + { + return null; + } + + return new IdentityEntity + { + Employee_Id = srcIdentity.Employee_Id, + Account = srcIdentity.Account, + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreatedAt = srcIdentity.CreatedAt, + CreatedBy = srcIdentity.CreatedBy, + ModifiedAt = srcIdentity.ModifiedAt, + ModifiedBy = srcIdentity.ModifiedBy + }; + } + public static Address To(this AddressEntity srcAddress) + { + if (srcAddress == null) + { + return null; + } + + return new Address + { + Id = srcAddress.Id, + Employee_Id = srcAddress.Employee_Id, + Country = srcAddress.Country, + Street = srcAddress.Street, + CreatedAt = srcAddress.CreatedAt, + CreatedBy = srcAddress.CreatedBy, + ModifiedAt = srcAddress.ModifiedAt, + ModifiedBy = srcAddress.ModifiedBy, + Remark = srcAddress.Remark + }; + } + public static AddressEntity To(this Address srcAddress) + { + if (srcAddress == null) + { + return null; + } + + return new AddressEntity + { + Id = srcAddress.Id, + Employee_Id = srcAddress.Employee_Id, + Country = srcAddress.Country, + Street = srcAddress.Street, + CreatedAt = srcAddress.CreatedAt, + CreatedBy = srcAddress.CreatedBy, + ModifiedAt = srcAddress.ModifiedAt, + ModifiedBy = srcAddress.ModifiedBy, + Remark = srcAddress.Remark + }; + } + public static IEnumerable
To(this IEnumerable srcProfiles) + { + return srcProfiles?.Select(p => p?.To()); + } + + public static IEnumerable To(this IEnumerable
srcProfiles) + { + return srcProfiles?.Select(p => p?.To()); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj new file mode 100644 index 00000000..def7c560 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..a76bc397 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,46 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ChangeTracking.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => { builder.AddConsole(); }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + ; + }); + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..d93a85cc --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,32 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + private string _employeeDbConnectionString; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs new file mode 100644 index 00000000..1bbc95fc --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +public class Address : IEntity +{ + public Guid Id { get; set; } + + public Guid Employee_Id { get; set; } + + public Employee Employee { get; set; } + + public string Country { get; set; } + + public string Street { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int SequenceId { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..dd1e6721 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Lab.ChangeTracking.Abstract; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Employee : IEntity + { + public Guid Id { get; set; } + + public int Version { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + public IList
Addresses { get; set; } + + public Identity Identity { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..d16a1f19 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,120 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identity { get; set; } + + public virtual DbSet
Addresses { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + // 改用 CI 執行 Migrate + + // if (s_migrated[0]) + // { + // return; + // } + // + // lock (s_migrated) + // { + // if (s_migrated[0] == false) + // { + // var sqlOptions = options.FindExtension(); + // if (sqlOptions != null) + // { + // this.Database.Migrate(); + // } + // + // s_migrated[0] = true; + // } + // } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); + modelBuilder.ApplyConfiguration(new IdentityConfiguration()); + modelBuilder.ApplyConfiguration(new ProfileConfiguration()); + } + + internal class EmployeeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Employee"); + builder.HasKey(p => p.Id) + .IsClustered(false); + + builder.Property(p => p.Name).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + + private class IdentityConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Identity"); + builder.HasKey(p => p.Employee_Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithOne(p => p.Identity) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade) + ; + + builder.Property(p => p.Account).IsRequired(); + builder.Property(p => p.Password).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + + private class ProfileConfiguration : IEntityTypeConfiguration
+ { + public void Configure(EntityTypeBuilder
builder) + { + builder.ToTable("Profile"); + builder.HasKey(p => p.Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithMany(p => p.Addresses) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade); + + builder.Property(p => p.Country).IsRequired(); + builder.Property(p => p.Street).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..e9e2f6d9 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Identity + { + public Guid Employee_Id { get; set; } + + // [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string? ModifiedBy { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..b115d8a2 --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj new file mode 100644 index 00000000..d944728b --- /dev/null +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + From 246e2239508be19b6a39e73fe992e6b5ad0a12b7 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 6 Mar 2022 11:22:49 +0800 Subject: [PATCH 170/267] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E7=84=A1?= =?UTF-8?q?=E6=B3=95=E5=AD=98=E6=AA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 14 ++--- .../Employee/Entity/AddressEntity.cs | 2 +- .../Employee/Entity/EmployeeEntity.cs | 6 ++- .../Employee/Entity/IdentityEntity.cs | 7 ++- .../Employee/Repository/RepositoryBase.cs | 51 ++++++++++++++++--- .../EntityModel/EmployeeDbContext.cs | 6 +-- 6 files changed, 65 insertions(+), 21 deletions(-) diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index ffac8aec..bd02c7dd 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -24,24 +24,26 @@ public class ChangeTrackingUnitTest [TestMethod] public void 異動追蹤後存檔() { - var toDB = Insert().To(); - var trackable = toDB.AsTrackable(); + var employeeEntity = Insert().To(); + var trackable = employeeEntity.AsTrackable(); trackable.Age = 20; trackable.Name = "小章"; + trackable.Remark = "我變了"; trackable.Identity.Remark = "我變了"; trackable.Addresses[0].Remark = "我變了"; + trackable.Addresses[1].Remark = "我掉了"; trackable.Addresses.RemoveAt(1); trackable.Addresses.Add(new AddressEntity() { Id = Guid.NewGuid(), - Employee_Id = toDB.Id, + Employee_Id = employeeEntity.Id, CreatedAt = DateTimeOffset.Now, CreatedBy = "sys", - Country = "Taipei", - Street = "Street", + Country = "Taipei1", + Street = "Street1", Remark = "我新的" }); - var employeeEntity = this._employeeRepository.SaveChangeAsync(trackable).Result; + var count = this._employeeRepository.SaveChangeAsync(trackable).Result; } [TestMethod] diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs index 67249ce5..349c5663 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs @@ -2,7 +2,7 @@ namespace Lab.ChangeTracking.Domain; -public record AddressEntity:IEntity +public record AddressEntity : IEntity { public virtual Guid Id { get; set; } diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs index 5bd37ec2..f27dd033 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs @@ -1,6 +1,8 @@ -namespace Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Abstract; -public record EmployeeEntity +namespace Lab.ChangeTracking.Domain; + +public record EmployeeEntity : IEntity { public virtual Guid Id { get; set; } diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs index 96092ab9..6594aa0d 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs @@ -1,6 +1,8 @@ -namespace Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Abstract; -public record IdentityEntity +namespace Lab.ChangeTracking.Domain; + +public record IdentityEntity { public virtual Guid Employee_Id { get; set; } @@ -17,4 +19,5 @@ public record IdentityEntity public virtual string? ModifiedBy { get; set; } public virtual string Remark { get; set; } + } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs index bb60a73e..ea01bf76 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs @@ -14,17 +14,19 @@ protected void ApplyChange(DbContext dbContext, where TTarget : class { var sourceTrackable = source.CastToIChangeTrackable(); + var targetInstance = CreateTargetInstance(source, excludeProperties); + dbContext.Set().Attach(targetInstance); + var changedProperties = sourceTrackable.ChangedProperties; - dbContext.Set().Attach(target); - foreach (var property in changedProperties) + foreach (var changedProperty in changedProperties) { if (excludeProperties != null - && excludeProperties.Any(p => p == property)) + && excludeProperties.Any(p => p == changedProperty)) { continue; } - dbContext.Entry(target).Property(property).IsModified = true; + dbContext.Entry(targetInstance).Property(changedProperty).IsModified = true; } } @@ -42,9 +44,10 @@ protected void ApplyChanges(DbContext dbContext, return; } + var modifyItems = targetsTrackable.ChangedItems; var addedItems = targetsTrackable.AddedItems; var deletedItems = targetsTrackable.DeletedItems; - foreach (var source in targetsTrackable.ChangedItems) + foreach (var source in modifyItems) { var target = targets.FirstOrDefault(p => p.Id == source.Id); if (target == null) @@ -57,8 +60,8 @@ protected void ApplyChanges(DbContext dbContext, foreach (var addedItem in addedItems) { - - // dbContext.Add(addedItem as TTarget); + var targetInstance = CreateTargetInstance(addedItem,excludeProperties); + dbContext.Entry(targetInstance).State = EntityState.Added; } foreach (var source in deletedItems) @@ -69,4 +72,38 @@ protected void ApplyChanges(DbContext dbContext, dbContext.Entry(target).State = EntityState.Deleted; } } + + private static TTarget CreateTargetInstance(TSource sourceInstance, + IEnumerable excludeProperties = null) + where TSource : class + where TTarget : class + { + var targetType = typeof(TTarget); + var targetInstance = (TTarget)Activator.CreateInstance(targetType); + var targetProperties = targetInstance.GetType().GetProperties(); + var sourceType = typeof(TSource); + var sourceProperties = sourceType.GetProperties(); + foreach (var sourceProperty in sourceProperties) + { + if (excludeProperties != null && + excludeProperties.Contains(sourceProperty.Name)) + { + continue; + } + + foreach (var targetProperty in targetProperties) + { + if (sourceProperty.Name == targetProperty.Name + & sourceProperty.PropertyType == targetProperty.PropertyType + ) + { + var value = sourceProperty.GetValue(sourceInstance, null); + targetProperty.SetValue(targetInstance, value, null); + break; + } + } + } + + return targetInstance; + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index d16a1f19..9ecfbc28 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -42,7 +42,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); modelBuilder.ApplyConfiguration(new IdentityConfiguration()); - modelBuilder.ApplyConfiguration(new ProfileConfiguration()); + modelBuilder.ApplyConfiguration(new AddressConfiguration()); } internal class EmployeeConfiguration : IEntityTypeConfiguration @@ -92,11 +92,11 @@ public void Configure(EntityTypeBuilder builder) } } - private class ProfileConfiguration : IEntityTypeConfiguration
+ private class AddressConfiguration : IEntityTypeConfiguration
{ public void Configure(EntityTypeBuilder
builder) { - builder.ToTable("Profile"); + builder.ToTable("Address"); builder.HasKey(p => p.Id).IsClustered(false); builder.HasOne(e => e.Employee) .WithMany(p => p.Addresses) From 1c7955253c71729feaf0af7cc76fbd8669acd18c Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 6 Mar 2022 11:52:37 +0800 Subject: [PATCH 171/267] refactor --- .../Lab.ChangeTracking.Abstract/IEntity.cs | 12 +-- .../ChangeTrackingUnitTest.cs | 76 ++++++++------- .../Employee/Entity/AddressEntity.cs | 5 +- .../Employee/Entity/EmployeeEntity.cs | 6 +- .../Employee/Entity/IdentityEntity.cs | 3 +- .../Employee/Repository/EmployeeRepository.cs | 7 +- .../Employee/Repository/RepositoryBase.cs | 97 +++++++++++-------- .../EntityModel/Address.cs | 3 +- .../EntityModel/Employee.cs | 3 +- 9 files changed, 117 insertions(+), 95 deletions(-) diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs index a861cb96..1076bfc0 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Abstract/IEntity.cs @@ -1,6 +1,6 @@ -namespace Lab.ChangeTracking.Abstract; - -public interface IEntity -{ - Guid Id { get; set; } -} \ No newline at end of file +// namespace Lab.ChangeTracking.Abstract; +// +// public interface IEntity +// { +// Guid Id { get; set; } +// } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index bd02c7dd..345a17be 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -31,7 +31,6 @@ public void 異動追蹤後存檔() trackable.Remark = "我變了"; trackable.Identity.Remark = "我變了"; trackable.Addresses[0].Remark = "我變了"; - trackable.Addresses[1].Remark = "我掉了"; trackable.Addresses.RemoveAt(1); trackable.Addresses.Add(new AddressEntity() { @@ -39,46 +38,57 @@ public void 異動追蹤後存檔() Employee_Id = employeeEntity.Id, CreatedAt = DateTimeOffset.Now, CreatedBy = "sys", - Country = "Taipei1", - Street = "Street1", + Country = "Taipei", + Street = "Street", Remark = "我新的" }); var count = this._employeeRepository.SaveChangeAsync(trackable).Result; + var dbContext = this._employeeDbContextFactory.CreateDbContext(); + var actual = dbContext.Employees + .Where(p => p.Id == employeeEntity.Id) + .Include(p => p.Identity) + .Include(p => p.Addresses) + .First() + ; + Assert.AreEqual("我變了",actual.Remark); + Assert.AreEqual("我變了",actual.Identity.Remark); + Assert.AreEqual("我變了",actual.Addresses[0].Remark); + Assert.AreEqual("我新的",actual.Addresses[1].Remark); } [TestMethod] public void 異動追蹤後存檔_回傳不可變的物件() { - var toDB = Insert(); - var source = new EmployeeEntity - { - Id = toDB.Id, - Name = "yao", - Age = 12, - Identity = new IdentityEntity - { - Employee_Id = toDB.Identity.Employee_Id - }, - Addresses = new List - { - new() - { - Id = toDB.Addresses[0] - .Id, - Employee_Id = toDB.Id, - Remark = "AAA" - }, - new() - { - Id = toDB.Addresses[1] - .Id, - Employee_Id = toDB.Id, - Remark = "AAA" - } - } - }; - var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; - this.DataShouldOk(source); + // var toDB = Insert(); + // var source = new EmployeeEntity + // { + // Id = toDB.Id, + // Name = "yao", + // Age = 12, + // Identity = new IdentityEntity + // { + // Employee_Id = toDB.Identity.Employee_Id + // }, + // Addresses = new List + // { + // new() + // { + // Id = toDB.Addresses[0] + // .Id, + // Employee_Id = toDB.Id, + // Remark = "AAA" + // }, + // new() + // { + // Id = toDB.Addresses[1] + // .Id, + // Employee_Id = toDB.Id, + // Remark = "AAA" + // } + // } + // }; + // var employeeEntity = this._employeeAggregate.ModifyFlowAsync(source).Result; + // this.DataShouldOk(source); } private void DataShouldOk(EmployeeEntity source) diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs index 349c5663..5e8d45e1 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/AddressEntity.cs @@ -1,8 +1,7 @@ -using Lab.ChangeTracking.Abstract; - + namespace Lab.ChangeTracking.Domain; -public record AddressEntity : IEntity +public record AddressEntity { public virtual Guid Id { get; set; } diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs index f27dd033..5bd37ec2 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/EmployeeEntity.cs @@ -1,8 +1,6 @@ -using Lab.ChangeTracking.Abstract; +namespace Lab.ChangeTracking.Domain; -namespace Lab.ChangeTracking.Domain; - -public record EmployeeEntity : IEntity +public record EmployeeEntity { public virtual Guid Id { get; set; } diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs index 6594aa0d..8982147c 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Entity/IdentityEntity.cs @@ -1,5 +1,4 @@ -using Lab.ChangeTracking.Abstract; - + namespace Lab.ChangeTracking.Domain; public record IdentityEntity diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs index 712b70fc..cde717bc 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/EmployeeRepository.cs @@ -16,15 +16,14 @@ public async Task SaveChangeAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) { await using var dbContext = await this._memberContextFactory.CreateDbContextAsync(cancel); - var destEmployee = srcEmployee.To(); - this.ApplyChange(dbContext, srcEmployee, destEmployee, new List + this.ApplyModify(dbContext, srcEmployee, new List { "Identity", "Addresses" } ); - this.ApplyChange(dbContext, srcEmployee.Identity, destEmployee.Identity); - this.ApplyChanges(dbContext, srcEmployee.Addresses, destEmployee.Addresses); + this.ApplyModify(dbContext, srcEmployee.Identity); + this.ApplyChanges(dbContext, srcEmployee.Addresses); return await dbContext.SaveChangesAsync(cancel); } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs index ea01bf76..64fbb6ee 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs @@ -1,41 +1,33 @@ using ChangeTracking; -using Lab.ChangeTracking.Abstract; using Microsoft.EntityFrameworkCore; namespace Lab.ChangeTracking.Domain; public class RepositoryBase { - protected void ApplyChange(DbContext dbContext, - TSource source, - TTarget target, - IEnumerable excludeProperties = null) + protected void ApplyAdd(DbContext dbContext, + TSource sourceInstance, + IEnumerable excludeProperties = null) where TSource : class where TTarget : class { - var sourceTrackable = source.CastToIChangeTrackable(); - var targetInstance = CreateTargetInstance(source, excludeProperties); - dbContext.Set().Attach(targetInstance); - - var changedProperties = sourceTrackable.ChangedProperties; - foreach (var changedProperty in changedProperties) - { - if (excludeProperties != null - && excludeProperties.Any(p => p == changedProperty)) - { - continue; - } + var targetInstance = CreateNewInstance(sourceInstance, excludeProperties); + dbContext.Entry(targetInstance).State = EntityState.Added; + } - dbContext.Entry(targetInstance).Property(changedProperty).IsModified = true; - } + protected void ApplyAdd(DbContext dbContext, TSource source) + where TSource : class where TTarget : class + { + var targetInstance = CreateDeleteInstance(source, "Id"); + dbContext.Set().Attach(targetInstance); + dbContext.Entry(targetInstance).State = EntityState.Deleted; } protected void ApplyChanges(DbContext dbContext, IList sources, - IList targets, IEnumerable excludeProperties = null) - where TSource : class, IEntity - where TTarget : class, IEntity + where TSource : class + where TTarget : class { var targetsTrackable = sources.CastToIChangeTrackableCollection(); @@ -49,32 +41,60 @@ protected void ApplyChanges(DbContext dbContext, var deletedItems = targetsTrackable.DeletedItems; foreach (var source in modifyItems) { - var target = targets.FirstOrDefault(p => p.Id == source.Id); - if (target == null) - { - continue; - } - - this.ApplyChange(dbContext, source, target, excludeProperties); + this.ApplyModify(dbContext, source, excludeProperties); } foreach (var addedItem in addedItems) { - var targetInstance = CreateTargetInstance(addedItem,excludeProperties); - dbContext.Entry(targetInstance).State = EntityState.Added; + this.ApplyAdd(dbContext, addedItem, excludeProperties); } foreach (var source in deletedItems) { - var target = Activator.CreateInstance(); - target.Id = source.Id; - dbContext.Set().Attach(target); - dbContext.Entry(target).State = EntityState.Deleted; + this.ApplyAdd(dbContext, source); + } + } + + protected void ApplyModify(DbContext dbContext, + TSource source, + IEnumerable excludeProperties = null) + where TSource : class + where TTarget : class + { + var sourceTrackable = source.CastToIChangeTrackable(); + var targetInstance = CreateNewInstance(source, excludeProperties); + dbContext.Set().Attach(targetInstance); + + var changedProperties = sourceTrackable.ChangedProperties; + foreach (var changedProperty in changedProperties) + { + if (excludeProperties != null + && excludeProperties.Any(p => p == changedProperty)) + { + continue; + } + + dbContext.Entry(targetInstance).Property(changedProperty).IsModified = true; } } - private static TTarget CreateTargetInstance(TSource sourceInstance, - IEnumerable excludeProperties = null) + private static TTarget CreateDeleteInstance(TSource sourceInstance, string propertyName) + where TSource : class + where TTarget : class + { + var targetType = typeof(TTarget); + var targetInstance = (TTarget)Activator.CreateInstance(targetType); + var targetProperty = targetType.GetProperty(propertyName); + var sourceType = sourceInstance.GetType(); + var sourceProperty = sourceType.GetProperty(propertyName); + var value = sourceProperty.GetValue(sourceInstance, null); + targetProperty.SetValue(targetInstance, value, null); + + return targetInstance; + } + + private static TTarget CreateNewInstance(TSource sourceInstance, + IEnumerable excludeProperties = null) where TSource : class where TTarget : class { @@ -94,8 +114,7 @@ private static TTarget CreateTargetInstance(TSource sourceInst foreach (var targetProperty in targetProperties) { if (sourceProperty.Name == targetProperty.Name - & sourceProperty.PropertyType == targetProperty.PropertyType - ) + & sourceProperty.PropertyType == targetProperty.PropertyType) { var value = sourceProperty.GetValue(sourceInstance, null); targetProperty.SetValue(targetInstance, value, null); diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs index 1bbc95fc..cdddbfa0 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs @@ -1,9 +1,8 @@ using System.ComponentModel.DataAnnotations.Schema; -using Lab.ChangeTracking.Abstract; namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; -public class Address : IEntity +public class Address { public Guid Id { get; set; } diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs index dd1e6721..42bc21b0 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Lab.ChangeTracking.Abstract; namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel { - public class Employee : IEntity + public class Employee { public Guid Id { get; set; } From 337483374c2f18aa26a5d9fbbcf83399f2e3df87 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 6 Mar 2022 16:57:29 +0800 Subject: [PATCH 172/267] refactor --- .../Employee/Repository/RepositoryBase.cs | 2 +- .../AppDependencyInjectionExtensions.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs index 64fbb6ee..16d5ff97 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain/Employee/Repository/RepositoryBase.cs @@ -94,7 +94,7 @@ private static TTarget CreateDeleteInstance(TSource sourceInst } private static TTarget CreateNewInstance(TSource sourceInstance, - IEnumerable excludeProperties = null) + IEnumerable excludeProperties = null) where TSource : class where TTarget : class { diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs index a76bc397..9d906132 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -21,6 +21,8 @@ public static void AddEntityFramework(this IServiceCollection services) var connectionString = option.EmployeeDbConnectionString; var loggerFactory = provider.GetService(); optionsBuilder.UseSqlServer(connectionString) + .LogTo(Console.WriteLine) + .EnableSensitiveDataLogging() .UseLoggerFactory(loggerFactory) ; }); From af1f1340d0caeb963baa908da9336cdcdac0dd14 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 6 Mar 2022 19:15:02 +0800 Subject: [PATCH 173/267] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=80?= =?UTF-8?q?=E7=AD=86=E5=BE=8C=E5=AD=98=E6=AA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 98 +++++++++--- .../Lab.ChangeTracking.Domain.UnitTest.csproj | 1 - .../TestAssistants.cs | 1 + .../Lab.ChangeTracking.Domain/CommitState.cs | 8 + .../EmployeeAggregate/Entity/AddressEntity.cs | 22 +++ .../Entity/EmployeeEntity.cs | 55 +++++-- .../Entity/IdentityEntity.cs | 16 +- .../EmployeeAggregate/Entity/ProfileEntity.cs | 8 - .../Repository/EmployeeRepository.cs | 48 +++--- .../Repository/TypeConverterExtensions.cs | 144 +++++++++++++++++ .../Lab.ChangeTracking.Domain/EntityBase.cs | 123 ++++++++------- .../Lab.ChangeTracking.Domain/EntityState.cs | 3 +- .../Lab.ChangeTracking.Domain/IChangeState.cs | 10 ++ .../Lab.ChangeTracking.Domain/IChangeTime.cs | 24 +-- .../IChangeTrackable.cs | 21 +++ .../src/Lab.ChangeTracking.Domain/Survey.cs | 39 ----- .../AppDependencyInjectionExtensions.cs | 2 + .../EntityModel/Address.cs | 29 ++++ .../EntityModel/Employee.cs | 40 ++--- .../EntityModel/EmployeeDbContext.cs | 148 ++++++++++++------ .../EntityModel/Identity.cs | 40 ++--- .../EntityModel/OrderHistory.cs | 29 ---- ...ab.ChangeTracking.Infrastructure.DB.csproj | 8 +- 23 files changed, 601 insertions(+), 316 deletions(-) create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs delete mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeState.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs delete mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs delete mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 8a7736ca..54bbf1e8 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Unicode; @@ -6,61 +8,111 @@ using Lab.MultiTestCase.UnitTest; using Microsoft.EntityFrameworkCore; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Employee = Lab.ChangeTracking.Infrastructure.DB.EntityModel.Employee; namespace Lab.ChangeTracking.Domain.UnitTest; [TestClass] public class ChangeTrackingUnitTest { + private static readonly IAccessContext _accessContext = TestAssistants.AccessContext; + + private static readonly IUUIdProvider _uuIdProvider = TestAssistants.UUIdProvider; private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; private readonly IDbContextFactory _employeeDbContextFactory = TestAssistants.EmployeeDbContextFactory; - private ISystemClock _systemClock = TestAssistants.SystemClock; - - public static IAccessContext _accessContext = TestAssistants.AccessContext; + private readonly IEmployeeRepository _employeeRepository = TestAssistants.EmployeeRepository; - public static IUUIdProvider _uuIdProvider = TestAssistants.UUIdProvider; + private readonly ISystemClock _systemClock = TestAssistants.SystemClock; [TestMethod] - public void 追蹤() + public void 異動追蹤後存檔() { - var employeeEntity = new EmployeeEntity - { - Id = Guid.NewGuid(), - State = EntityState.Added, - Name = "yao", - Age = 19, - Remark = "remark" - }; - employeeEntity.InitialTrack(); - employeeEntity.SetProfile("小章", 20,"remark"); - employeeEntity.SetProfile("小章", 20,"remark"); + var employeeEntity = Insert().To(); + employeeEntity.AsTrackable(); + employeeEntity.SetProfile("小章", 19,"新來的"); + employeeEntity.AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + + var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + var dbContext = this._employeeDbContextFactory.CreateDbContext(); + + var actual = dbContext.Employees + .Where(p => p.Id == employeeEntity.Id) + .Include(p => p.Identity) + .Include(p => p.Addresses) + .First() + ; + Assert.AreEqual("小章", actual.Name); + Assert.AreEqual(19, actual.Age); + Assert.AreEqual("新來的", actual.Remark); + Assert.AreEqual("我新的", actual.Addresses[1].Remark); } [TestMethod] - public void 追蹤集合() + public void 新增一筆後存檔() { + var employeeEntity = new EmployeeEntity(); + employeeEntity.New("yao", 10); + employeeEntity.AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + + var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + var dbContext = this._employeeDbContextFactory.CreateDbContext(); + + var actual = dbContext.Employees + .Where(p => p.Id == employeeEntity.Id) + .Include(p => p.Identity) + .Include(p => p.Addresses) + .First() + ; + Assert.AreEqual("我變了", actual.Remark); + Assert.AreEqual("我變了", actual.Identity.Remark); + Assert.AreEqual("我變了", actual.Addresses[0].Remark); + Assert.AreEqual("我新的", actual.Addresses[1].Remark); } private static Employee Insert() { using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var employeeId = Guid.NewGuid(); var toDB = new Employee { - Id = Guid.NewGuid(), + Id = employeeId, Age = 18, Name = "yao", - CreateAt = DateTimeOffset.Now, - CreateBy = "TEST", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", Identity = new Identity { + Employee_Id = employeeId, Account = "yao", Password = "123456", - CreateAt = DateTimeOffset.Now, - CreateBy = "TEST" + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", + Remark = "編輯" + }, + Addresses = new List
+ { + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "修改的" + }, + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "刪除的" + } } }; dbContext.Employees.Add(toDB); diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj index fbfda1f2..e560c2d6 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -8,7 +8,6 @@ - diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs index af3c81ad..82708555 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -45,6 +45,7 @@ public static void ConfigureTestServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); _serviceProvider = services.BuildServiceProvider(); } diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs new file mode 100644 index 00000000..0ba74bce --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public enum CommitState +{ + Unchanged = 0, + Rejected = 1, + Accepted = 99, +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs new file mode 100644 index 00000000..6b6a7214 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs @@ -0,0 +1,22 @@ +namespace Lab.ChangeTracking.Domain; + +public record AddressEntity +{ + public Guid Id { get; set; } + + public Guid Employee_Id { get; set; } + + public string Country { get; set; } + + public string Street { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index ebb87622..83ab26e4 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -1,4 +1,6 @@ -namespace Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain; public record EmployeeEntity : EntityBase { @@ -20,28 +22,55 @@ public string Remark init => this._remark = value; } - private IAccessContext _accessContext; - private int? _age; + public List Addresses { get; init; } - // public IList Profiles { get; private set; } = new(); - // - // public IdentityEntity Identity { get; private set; } = new(); + public IdentityEntity Identity { get; init; } - private IUUIdProvider _idProvider; + private int? _age; private string _name; private string _remark; - private ISystemClock _systemClock; - public EmployeeEntity SetId(string name, int age, string remark = null) + public EmployeeEntity Delete() + { + this._entityState = EntityState.Deleted; + return this; + } + + public EmployeeEntity New(string name, int age, string remark = null) { + this._entityState = EntityState.Added; + this._commitState = CommitState.Unchanged; + this._version = 1; this._name = name; this._age = age; this._remark = remark; - this.ChangeTrack(nameof(this.Name), name); - this.ChangeTrack(nameof(this.Age), age); - this.ChangeTrack(nameof(this.Remark), remark); return this; - } + } + + public override void Reset() + { + throw new NotImplementedException(); + } + + /// + /// 從資料庫查到之後放進去 + /// + /// + /// + public EmployeeEntity AsTrackable(Employee employee) + { + this._changedProperties.Clear(); + this._originalValues.Clear(); + this._entityState = EntityState.Unchanged; + this._commitState = CommitState.Unchanged; + this._version = employee.Version; + this._name = employee.Name; + this._age = employee.Age; + this._remark = employee.Remark; + this.AsTrackable(); + return this; + } + public EmployeeEntity SetProfile(string name, int age, string remark = null) { this._name = name; diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs index 1435576f..bcdde865 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -2,13 +2,19 @@ public record IdentityEntity { - public virtual string Account { get; set; } + public Guid Employee_Id { get; set; } - public virtual string Password { get; set; } + public string Account { get; set; } - public virtual string Remark { get; set; } + public string Password { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } - public virtual DateTimeOffset CreateAt { get; set; } + public DateTimeOffset? ModifiedAt { get; set; } - public virtual string CreateBy { get; set; } + public string? ModifiedBy { get; set; } + + public virtual string Remark { get; set; } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs deleted file mode 100644 index 8ab5d414..00000000 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/ProfileEntity.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Lab.ChangeTracking.Domain; - -public record ProfileEntity -{ - public virtual string FirstName { get; set; } - - public virtual string LastName { get; set; } -} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 97f22e66..0738cfc8 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -5,42 +5,36 @@ namespace Lab.ChangeTracking.Domain; public class EmployeeRepository : IEmployeeRepository { - private readonly IDbContextFactory _memberContextFactory; + private readonly IDbContextFactory _employeeDbContextFactory; public EmployeeRepository(IDbContextFactory memberContextFactory) { - this._memberContextFactory = memberContextFactory; + this._employeeDbContextFactory = memberContextFactory; } - public Infrastructure.DB.EntityModel.Employee To(EmployeeEntity srcEmployee) + public async Task SaveChangeAsync(EmployeeEntity srcEmployee, + CancellationToken cancel = default) { - return new Infrastructure.DB.EntityModel.Employee + if (srcEmployee.CommitState != CommitState.Accepted) { - // Id = srcEmployee.Id, - // Name = srcEmployee.Name, - // Age = srcEmployee.Age, - // Remark = srcEmployee.Remark, - // CreateAt = srcEmployee.CreatedAt + throw new Exception($"{nameof(srcEmployee)} 尚未核准,不得儲存"); + } - // CreateBy = srcEmployee.CreatedBy, - // Identity = this.To(srcEmployee.Identity) - }; - } - - public Identity To(IdentityEntity srcIdentity) - { - return new Identity + await using var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + switch (srcEmployee.EntityState) { - Password = srcIdentity.Password, - Remark = srcIdentity.Remark, - CreateAt = srcIdentity.CreateAt, - CreateBy = srcIdentity.CreateBy - }; - } + case EntityState.Added: + dbContext.Set().Add(srcEmployee.To()); + break; + case EntityState.Modified: + break; + case EntityState.Deleted: + break; + + default: + throw new ArgumentOutOfRangeException(); + } - public async Task SaveChangeAsync(EmployeeEntity srcEmployee, - CancellationToken cancel = default) - { - throw new Exception(); + return await dbContext.SaveChangesAsync(cancel); } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs new file mode 100644 index 00000000..5cf3fe8c --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs @@ -0,0 +1,144 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain; + +public static class TypeConverterExtensions +{ + public static Employee To(this EmployeeEntity srcEmployee) + { + if (srcEmployee == null) + { + return null; + } + + return new Employee + { + Id = srcEmployee.Id ?? default, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Version = srcEmployee.Version, + Remark = srcEmployee.Remark, + Addresses = srcEmployee.Addresses != null ? srcEmployee.Addresses.To().ToList() : null, + Identity = srcEmployee.Identity.To(), + CreatedAt = srcEmployee.CreatedAt ?? default, + CreatedBy = srcEmployee.CreatedBy, + ModifiedAt = srcEmployee.ModifiedAt, + ModifiedBy = srcEmployee.ModifiedBy + }; + } + + public static EmployeeEntity To(this Employee srcEmployee) + { + if (srcEmployee == null) + { + return null; + } + + return new EmployeeEntity + { + Id = srcEmployee.Id, + Name = srcEmployee.Name, + Age = srcEmployee.Age, + Version = srcEmployee.Version, + Remark = srcEmployee.Remark, + Addresses = srcEmployee.Addresses.To()?.ToList(), + Identity = srcEmployee.Identity.To(), + CreatedAt = srcEmployee.CreatedAt, + CreatedBy = srcEmployee.CreatedBy, + ModifiedAt = srcEmployee.ModifiedAt, + ModifiedBy = srcEmployee.ModifiedBy + }; + } + + public static Identity To(this IdentityEntity srcIdentity) + { + if (srcIdentity == null) + { + return null; + } + + return new Identity + { + Employee_Id = srcIdentity.Employee_Id, + Account = srcIdentity.Account, + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreatedAt = srcIdentity.CreatedAt, + CreatedBy = srcIdentity.CreatedBy, + ModifiedAt = srcIdentity.ModifiedAt, + ModifiedBy = srcIdentity.ModifiedBy + }; + } + + public static IdentityEntity To(this Identity srcIdentity) + { + if (srcIdentity == null) + { + return null; + } + + return new IdentityEntity + { + Employee_Id = srcIdentity.Employee_Id, + Account = srcIdentity.Account, + Password = srcIdentity.Password, + Remark = srcIdentity.Remark, + CreatedAt = srcIdentity.CreatedAt, + CreatedBy = srcIdentity.CreatedBy, + ModifiedAt = srcIdentity.ModifiedAt, + ModifiedBy = srcIdentity.ModifiedBy + }; + } + + public static Address To(this AddressEntity srcAddress) + { + if (srcAddress == null) + { + return null; + } + + return new Address + { + Id = srcAddress.Id, + Employee_Id = srcAddress.Employee_Id, + Country = srcAddress.Country, + Street = srcAddress.Street, + CreatedAt = srcAddress.CreatedAt, + CreatedBy = srcAddress.CreatedBy, + ModifiedAt = srcAddress.ModifiedAt, + ModifiedBy = srcAddress.ModifiedBy, + Remark = srcAddress.Remark + }; + } + + public static AddressEntity To(this Address srcAddress) + { + if (srcAddress == null) + { + return null; + } + + return new AddressEntity + { + Id = srcAddress.Id, + Employee_Id = srcAddress.Employee_Id, + Country = srcAddress.Country, + Street = srcAddress.Street, + CreatedAt = srcAddress.CreatedAt, + CreatedBy = srcAddress.CreatedBy, + ModifiedAt = srcAddress.ModifiedAt, + ModifiedBy = srcAddress.ModifiedBy, + Remark = srcAddress.Remark + }; + } + + public static IEnumerable
To(this IEnumerable srcProfiles) + { + return srcProfiles?.Select(p => p?.To()); + } + + public static IEnumerable To(this IEnumerable
srcProfiles) + { + return srcProfiles?.Select(p => p?.To()); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs index 7e675b00..68926968 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs @@ -1,6 +1,6 @@ namespace Lab.ChangeTracking.Domain; -public record EntityBase : IChangeTrackable +public abstract record EntityBase : IChangeTrackable { public Guid? Id { @@ -8,35 +8,48 @@ public Guid? Id init => this._id = value; } - private readonly Dictionary _changedProperties = new(); - private readonly Dictionary _originalValues = new(); + protected readonly Dictionary _changedProperties = new(); + protected readonly Dictionary _originalValues = new(); + protected CommitState _commitState; private DateTimeOffset? _createdAt; private string? _createdBy; + protected EntityState _entityState; private Guid? _id; private DateTimeOffset? _modifiedAt; private string? _modifiedBy; - private EntityState _state; - private int _version; + protected int _version; + + public EntityBase AsTrackable() + { + this.Validate(); + + // this._entityState = EntityState.Added; + // this._commitState = CommitState.Unchanged; + // this._version = 1; + var properties = this.GetType().GetProperties(); + foreach (var property in properties) + { + this._originalValues.Add(property.Name, property.GetValue(this)); + } + + return this; + } public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, IAccessContext accessContext, IUUIdProvider idProvider) { - if (this.State == EntityState.Submitted) - { - return ( - new Error("STATE_CONFLICT", - $"Entity({this.State}) was submitted and should not submit again."), false); - } + this.Validate(); + this._commitState = CommitState.Accepted; var (now, accessUserId) = (systemClock.GetNow(), accessContext.GetUserId()); - if (this.State == EntityState.Unchanged) + if (this.EntityState == EntityState.Unchanged) { return (null, false); } - if (this.State == EntityState.Added) + if (this.EntityState == EntityState.Added) { this._id = idProvider.GenerateId(); this._createdAt = now; @@ -51,33 +64,28 @@ public Guid? Id this._modifiedAt = now; this._modifiedBy = accessUserId; - this._state = EntityState.Submitted; + // this._entityState = EntityState.Submitted; return (null, true); } - public void InitialTrack() - { - this._state = EntityState.Added; - this._version = 1; - var properties = this.GetType().GetProperties(); - foreach (var property in properties) - { - this._originalValues.Add(property.Name, property.GetValue(this)); - } - } + public abstract void Reset(); public Dictionary GetChangedProperties() { return this._changedProperties; } - public bool HasChanged { get; private set; } + public EntityState EntityState + { + get => this._entityState; + init => this._entityState = value; + } - public EntityState State + public CommitState CommitState { - get => this._state; - init => this._state = value; + get => this._commitState; + init => this._commitState = value; } public int Version @@ -91,33 +99,6 @@ public Dictionary GetOriginalValues() return this._originalValues; } - protected void ChangeTrack(string propertyName, object value) - { - if (this.State == EntityState.Submitted) - { - throw new Exception("已經 Submitted ,無法再進行修改。"); - } - - var changes = this._changedProperties; - var originals = this._originalValues; - if (changes.ContainsKey(propertyName) == false) - { - if (originals[propertyName] != value) - { - changes.Add(propertyName, value); - this._state = EntityState.Added; - } - } - else - { - if (originals[propertyName] != changes[propertyName]) - { - changes[propertyName] = value; - this._state = EntityState.Modified; - } - } - } - public DateTimeOffset? CreatedAt { get => this._createdAt; @@ -141,4 +122,36 @@ public string? ModifiedBy get => this._modifiedBy; init => this._modifiedBy = value; } + + public void ChangeTrack(string propertyName, object value) + { + this.Validate(); + + var changes = this._changedProperties; + var originals = this._originalValues; + if (changes.ContainsKey(propertyName) == false) + { + if (originals[propertyName] != value) + { + changes.Add(propertyName, value); + this._entityState = EntityState.Added; + } + } + else + { + if (originals[propertyName] != changes[propertyName]) + { + changes[propertyName] = value; + this._entityState = EntityState.Modified; + } + } + } + + private void Validate() + { + if (this.CommitState == CommitState.Accepted) + { + throw new Exception("已經同意,無法再進行修改"); + } + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs index 2573f480..ee413431 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs @@ -5,5 +5,6 @@ public enum EntityState Unchanged = 0, Added = 1, Modified = 2, - Submitted = 99, + Deleted=3, + // Submitted = 99, } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeState.cs new file mode 100644 index 00000000..90225b7f --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeState.cs @@ -0,0 +1,10 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeState +{ + EntityState EntityState { get; init; } + + CommitState CommitState { get; init; } + + int Version { get; init; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs index c16ef976..a0f626c9 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs @@ -1,4 +1,6 @@ -namespace Lab.ChangeTracking.Domain; +using System.ComponentModel; + +namespace Lab.ChangeTracking.Domain; public interface IChangeTime { @@ -9,24 +11,4 @@ public interface IChangeTime DateTimeOffset? ModifiedAt { get; init; } string? ModifiedBy { get; init; } -} - -public interface IChangeState -{ - EntityState State { get; init; } - - int Version { get; init; } -} - -public interface IChangeTrackable : IChangeTime, IChangeState -{ - bool HasChanged { get; } - - Dictionary GetChangedProperties(); - - Dictionary GetOriginalValues(); - - // void SetTrackable(); - // - // void ChangeTrack(string propertyName, object value); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs new file mode 100644 index 00000000..02ceeab3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs @@ -0,0 +1,21 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeTrackable : IChangeTime, IChangeState +{ + // bool HasChanged { get; } + public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, + IAccessContext accessContext, + IUUIdProvider idProvider); + + void ChangeTrack(string propertyName, object value); + + void Reset(); + + Dictionary GetChangedProperties(); + + Dictionary GetOriginalValues(); + + // void SetTrackable(); + // + // void ChangeTrack(string propertyName, object value); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs deleted file mode 100644 index 9a0f3d26..00000000 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/Survey.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.ComponentModel; -using System.Reflection; - -namespace Lab.ChangeTracking.Domain; - -public class Base : IRevertibleChangeTracking -{ - protected readonly Dictionary ChangedProperties = new(); - protected readonly Dictionary OriginalValues = new(); - - public void Initialize() - { - var properties = this.GetType().GetProperties(); - - // Save the current value of the properties to our dictionary. - foreach (var property in properties) - { - this.OriginalValues.Add(property.Name, property.GetValue(this)); - } - } - - public bool IsChanged { get; private set; } - - public void RejectChanges() - { - foreach (var property in this.ChangedProperties) - { - this.GetType().GetRuntimeProperty(property.Key).SetValue(this, property.Value); - } - - this.AcceptChanges(); - } - - public void AcceptChanges() - { - this.ChangedProperties.Clear(); - this.IsChanged = false; - } -} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs index a76bc397..332cf598 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -22,6 +22,8 @@ public static void AddEntityFramework(this IServiceCollection services) var loggerFactory = provider.GetService(); optionsBuilder.UseSqlServer(connectionString) .UseLoggerFactory(loggerFactory) + .LogTo(Console.WriteLine) + .EnableSensitiveDataLogging() ; }); diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs new file mode 100644 index 00000000..cdddbfa0 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +public class Address +{ + public Guid Id { get; set; } + + public Guid Employee_Id { get; set; } + + public Employee Employee { get; set; } + + public string Country { get; set; } + + public string Street { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int SequenceId { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs index 2e37227a..42bc21b0 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -1,30 +1,34 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; - -[Table("Employee")] -public class Employee +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Id { get; set; } + public class Employee + { + public Guid Id { get; set; } + + public int Version { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + public IList
Addresses { get; set; } - [Required] - public string Name { get; set; } + public Identity Identity { get; set; } - public int? Age { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } + public DateTimeOffset CreatedAt { get; set; } - public string Remark { get; set; } + public string CreatedBy { get; set; } - [Required] - public DateTimeOffset CreateAt { get; set; } + public DateTimeOffset? ModifiedAt { get; set; } - [Required] - public string CreateBy { get; set; } + public string ModifiedBy { get; set; } - public virtual Identity Identity { get; set; } + public string Remark { get; set; } + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs index f859963d..9ecfbc28 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -1,76 +1,120 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Builders; -namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; - -public class EmployeeDbContext : DbContext +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel { - private static readonly bool[] s_migrated = { false }; + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; - public virtual DbSet Employees { get; set; } + public virtual DbSet Employees { get; set; } - public virtual DbSet Identities { get; set; } + public virtual DbSet Identity { get; set; } - public virtual DbSet OrderHistories { get; set; } + public virtual DbSet
Addresses { get; set; } - public EmployeeDbContext(DbContextOptions options) - : base(options) - { - if (s_migrated[0]) + public EmployeeDbContext(DbContextOptions options) + : base(options) { - return; + // 改用 CI 執行 Migrate + + // if (s_migrated[0]) + // { + // return; + // } + // + // lock (s_migrated) + // { + // if (s_migrated[0] == false) + // { + // var sqlOptions = options.FindExtension(); + // if (sqlOptions != null) + // { + // this.Database.Migrate(); + // } + // + // s_migrated[0] = true; + // } + // } } - lock (s_migrated) + protected override void OnModelCreating(ModelBuilder modelBuilder) { - if (s_migrated[0] == false) + modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); + modelBuilder.ApplyConfiguration(new IdentityConfiguration()); + modelBuilder.ApplyConfiguration(new AddressConfiguration()); + } + + internal class EmployeeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) { - var memoryOptions = options.FindExtension(); - - if (memoryOptions == null) - { - var sqlOptions = options.FindExtension(); - if (sqlOptions != null) - { - this.Database.Migrate(); - } - } - - s_migrated[0] = true; + builder.ToTable("Employee"); + builder.HasKey(p => p.Id) + .IsClustered(false); + + builder.Property(p => p.Name).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); } } - } - //管理索引 - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(p => + private class IdentityConfiguration : IEntityTypeConfiguration { - p.HasKey(e => e.Id) - .IsClustered(false); + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Identity"); + builder.HasKey(p => p.Employee_Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithOne(p => p.Identity) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade) + ; - p.HasIndex(e => e.SequenceId) - .IsUnique() - .IsClustered(); + builder.Property(p => p.Account).IsRequired(); + builder.Property(p => p.Password).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); - p.Property(p => p.Remark) - .IsRequired(false) - ; - }); + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } - modelBuilder.Entity(p => + private class AddressConfiguration : IEntityTypeConfiguration
{ - p.HasKey(e => e.Employee_Id) - .IsClustered(false); + public void Configure(EntityTypeBuilder
builder) + { + builder.ToTable("Address"); + builder.HasKey(p => p.Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithMany(p => p.Addresses) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade); - p.HasIndex(e => e.SequenceId) - .IsUnique() - .IsClustered(); + builder.Property(p => p.Country).IsRequired(); + builder.Property(p => p.Street).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); - p.Property(p => p.Remark) - .IsRequired(false) - ; - }); + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs index f2251c34..e9e2f6d9 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -1,32 +1,32 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; - -[Table("Identity")] -public class Identity +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.None)] - public Guid Employee_Id { get; set; } + public class Identity + { + public Guid Employee_Id { get; set; } + + // [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + + [Required] + public string Account { get; set; } - [Required] - public string Account { get; set; } + [Required] + public string Password { get; set; } - [Required] - public string Password { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } + public string Remark { get; set; } - public string Remark { get; set; } + public DateTimeOffset CreatedAt { get; set; } - [Required] - public DateTimeOffset CreateAt { get; set; } + public string CreatedBy { get; set; } - [Required] - public string CreateBy { get; set; } + public DateTimeOffset? ModifiedAt { get; set; } - [ForeignKey("Employee_Id")] - public virtual Employee Employee { get; set; } + public string? ModifiedBy { get; set; } + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs deleted file mode 100644 index 6632c4e0..00000000 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/OrderHistory.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; - -[Table("OrderHistory")] -public class OrderHistory -{ - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid Id { get; set; } - - public Guid? Employee_Id { get; set; } - - public string Remark { get; set; } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long SequenceId { get; set; } - - public string Product_Id { get; set; } - - public string Product_Name { get; set; } - - [Required] - public DateTime CreateAt { get; set; } - - [Required] - public string CreateBy { get; set; } -} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj index b1b261bb..9fcdb0d8 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -6,10 +6,10 @@ enable - - - - + + + + From 200f218dcbd9e94690faea00f8481f5689e60bb0 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 7 Mar 2022 01:12:44 +0800 Subject: [PATCH 174/267] =?UTF-8?q?feat:=20=E7=B7=A8=E8=BC=AF=E3=80=81?= =?UTF-8?q?=E5=88=AA=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 101 ++++++++++++++---- .../Entity/EmployeeEntity.cs | 50 +++++---- .../Repository/EmployeeRepository.cs | 48 ++++++++- .../Repository/IEmployeeRepository.cs | 4 +- .../Repository/TypeConverterExtensions.cs | 4 +- .../Lab.ChangeTracking.Domain/EntityBase.cs | 35 +++--- .../Lab.ChangeTracking.Domain/IChangeTime.cs | 4 +- .../PropertyChangeTracker.cs | 48 --------- 8 files changed, 188 insertions(+), 106 deletions(-) delete mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 54bbf1e8..91b6b981 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -4,6 +4,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Unicode; +using EFCore.BulkExtensions; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Lab.MultiTestCase.UnitTest; using Microsoft.EntityFrameworkCore; @@ -17,47 +18,101 @@ public class ChangeTrackingUnitTest private static readonly IAccessContext _accessContext = TestAssistants.AccessContext; private static readonly IUUIdProvider _uuIdProvider = TestAssistants.UUIdProvider; - private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; - private readonly IDbContextFactory _employeeDbContextFactory = + private static readonly IDbContextFactory s_employeeDbContextFactory = TestAssistants.EmployeeDbContextFactory; + private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + private readonly IEmployeeRepository _employeeRepository = TestAssistants.EmployeeRepository; private readonly ISystemClock _systemClock = TestAssistants.SystemClock; + [ClassCleanup] + public static void ClassCleanup() + { + DeleteAllTable(); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + DeleteAllTable(); + } + [TestMethod] - public void 異動追蹤後存檔() + public void 刪除一筆資料() { - var employeeEntity = Insert().To(); - employeeEntity.AsTrackable(); - employeeEntity.SetProfile("小章", 19,"新來的"); - employeeEntity.AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + var fromDb = Insert(); + var employeeEntity = new EmployeeEntity(); + employeeEntity.AsTrackable(fromDb) + .SetDelete() + .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; - var dbContext = this._employeeDbContextFactory.CreateDbContext(); + + Assert.AreEqual(1, count); + var dbContext = s_employeeDbContextFactory.CreateDbContext(); var actual = dbContext.Employees - .Where(p => p.Id == employeeEntity.Id) + .Where(p => p.Id == fromDb.Id) + .Include(p => p.Identity) + .Include(p => p.Addresses) + .FirstOrDefault() + ; + Assert.AreEqual(null, actual); + } + + [TestMethod] + public void 更新一筆資料() + { + var fromDb = Insert(); + var employeeEntity = new EmployeeEntity(); + employeeEntity.AsTrackable(fromDb) + .SetProfile("小章", 19, "我變了") + .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + + var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + + Assert.AreEqual(1, count); + var dbContext = s_employeeDbContextFactory.CreateDbContext(); + + var actual = dbContext.Employees + .Where(p => p.Id == fromDb.Id) .Include(p => p.Identity) .Include(p => p.Addresses) .First() ; Assert.AreEqual("小章", actual.Name); Assert.AreEqual(19, actual.Age); - Assert.AreEqual("新來的", actual.Remark); - Assert.AreEqual("我新的", actual.Addresses[1].Remark); + Assert.AreEqual("我變了", actual.Remark); } [TestMethod] - public void 新增一筆後存檔() + public void 沒有異動() { + var fromDb = Insert(); var employeeEntity = new EmployeeEntity(); - employeeEntity.New("yao", 10); - employeeEntity.AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + employeeEntity.AsTrackable(fromDb) + .SetProfile("小章", 19, "新來的") + .SetProfile("yao", 18, "編輯") + .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; - var dbContext = this._employeeDbContextFactory.CreateDbContext(); + Assert.AreEqual(0, count); + } + + [TestMethod] + public void 新增一筆資料() + { + var employeeEntity = new EmployeeEntity(); + employeeEntity.New("yao", 10, "新的") + .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + + var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + + Assert.AreEqual(1, count); + var dbContext = s_employeeDbContextFactory.CreateDbContext(); var actual = dbContext.Employees .Where(p => p.Id == employeeEntity.Id) @@ -65,10 +120,17 @@ public void 新增一筆後存檔() .Include(p => p.Addresses) .First() ; - Assert.AreEqual("我變了", actual.Remark); - Assert.AreEqual("我變了", actual.Identity.Remark); - Assert.AreEqual("我變了", actual.Addresses[0].Remark); - Assert.AreEqual("我新的", actual.Addresses[1].Remark); + Assert.AreEqual("yao", actual.Name); + Assert.AreEqual(10, actual.Age); + Assert.AreEqual("新的", actual.Remark); + } + + private static void DeleteAllTable() + { + var dbContext = s_employeeDbContextFactory.CreateDbContext(); + dbContext.Employees.BatchDelete(); + dbContext.Addresses.BatchDelete(); + dbContext.Identity.BatchDelete(); } private static Employee Insert() @@ -82,6 +144,7 @@ private static Employee Insert() Name = "yao", CreatedAt = DateTimeOffset.Now, CreatedBy = "Sys", + Remark = "編輯", Identity = new Identity { Employee_Id = employeeId, diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index 83ab26e4..d481562c 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -30,7 +30,36 @@ public string Remark private string _name; private string _remark; - public EmployeeEntity Delete() + /// + /// 從資料庫查到之後放進去 + /// + /// + /// + public EmployeeEntity AsTrackable(Employee employee) + { + this._changedProperties.Clear(); + this._originalValues.Clear(); + this._entityState = EntityState.Unchanged; + this._commitState = CommitState.Unchanged; + this._id = employee.Id; + this._version = employee.Version; + this._createdAt = employee.CreatedAt; + this._createdBy = employee.CreatedBy; + this._modifiedAt = employee.ModifiedAt; + this._modifiedBy = employee.ModifiedBy; + this._version = employee.Version; + this._name = employee.Name; + this._age = employee.Age; + this._remark = employee.Remark; + + // Addresses = null, + // Identity = null, + + this.AsTrackable(); + return this; + } + + public EmployeeEntity SetDelete() { this._entityState = EntityState.Deleted; return this; @@ -52,25 +81,6 @@ public override void Reset() throw new NotImplementedException(); } - /// - /// 從資料庫查到之後放進去 - /// - /// - /// - public EmployeeEntity AsTrackable(Employee employee) - { - this._changedProperties.Clear(); - this._originalValues.Clear(); - this._entityState = EntityState.Unchanged; - this._commitState = CommitState.Unchanged; - this._version = employee.Version; - this._name = employee.Name; - this._age = employee.Age; - this._remark = employee.Remark; - this.AsTrackable(); - return this; - } - public EmployeeEntity SetProfile(string name, int age, string remark = null) { this._name = name; diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs index 0738cfc8..2791fd8f 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -13,6 +13,7 @@ public EmployeeRepository(IDbContextFactory memberContextFact } public async Task SaveChangeAsync(EmployeeEntity srcEmployee, + IEnumerable excludeProperties = null, CancellationToken cancel = default) { if (srcEmployee.CommitState != CommitState.Accepted) @@ -24,17 +25,60 @@ public async Task SaveChangeAsync(EmployeeEntity srcEmployee, switch (srcEmployee.EntityState) { case EntityState.Added: - dbContext.Set().Add(srcEmployee.To()); + ApplyAdd(dbContext, srcEmployee); break; case EntityState.Modified: + ApplyModify(dbContext, srcEmployee, excludeProperties); + break; case EntityState.Deleted: + ApplyDelete(srcEmployee, dbContext); + break; - + + case EntityState.Unchanged: + return 0; default: throw new ArgumentOutOfRangeException(); } return await dbContext.SaveChangesAsync(cancel); } + + private static void ApplyDelete(EmployeeEntity srcEmployee, EmployeeDbContext dbContext) + { + dbContext.Set().Remove(new Employee() { Id = srcEmployee.Id }); + } + + private static void ApplyAdd(EmployeeDbContext dbContext, EmployeeEntity srcEmployee) + { + dbContext.Set().Add(srcEmployee.To()); + } + + private static void ApplyModify(EmployeeDbContext dbContext, + EmployeeEntity srcEmployee, + IEnumerable excludeProperties = null) + { + var destEmployee = new Employee() + { + Id = srcEmployee.Id + }; + + dbContext.Set().Attach(destEmployee); + var employeeEntry = dbContext.Entry(destEmployee); + + foreach (var property in srcEmployee.GetChangedProperties()) + { + var propertyName = property.Key; + var value = property.Value; + if (excludeProperties != null + && excludeProperties.Any(p => p == propertyName)) + { + continue; + } + + dbContext.Entry(destEmployee).Property(propertyName).CurrentValue = value; + employeeEntry.Property(propertyName).IsModified = true; + } + } } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs index 8748817c..779211fc 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -2,5 +2,7 @@ public interface IEmployeeRepository { - Task SaveChangeAsync(EmployeeEntity employee, CancellationToken cancel = default); + Task SaveChangeAsync(EmployeeEntity employee, + IEnumerable excludeProperties = null, + CancellationToken cancel = default); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs index 5cf3fe8c..4b0d422d 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs @@ -13,14 +13,14 @@ public static Employee To(this EmployeeEntity srcEmployee) return new Employee { - Id = srcEmployee.Id ?? default, + Id = srcEmployee.Id, Name = srcEmployee.Name, Age = srcEmployee.Age, Version = srcEmployee.Version, Remark = srcEmployee.Remark, Addresses = srcEmployee.Addresses != null ? srcEmployee.Addresses.To().ToList() : null, Identity = srcEmployee.Identity.To(), - CreatedAt = srcEmployee.CreatedAt ?? default, + CreatedAt = srcEmployee.CreatedAt, CreatedBy = srcEmployee.CreatedBy, ModifiedAt = srcEmployee.ModifiedAt, ModifiedBy = srcEmployee.ModifiedBy diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs index 68926968..d6477afb 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs @@ -1,8 +1,10 @@ -namespace Lab.ChangeTracking.Domain; +using System.Collections; + +namespace Lab.ChangeTracking.Domain; public abstract record EntityBase : IChangeTrackable { - public Guid? Id + public Guid Id { get => this._id; init => this._id = value; @@ -11,12 +13,12 @@ public Guid? Id protected readonly Dictionary _changedProperties = new(); protected readonly Dictionary _originalValues = new(); protected CommitState _commitState; - private DateTimeOffset? _createdAt; - private string? _createdBy; + protected DateTimeOffset _createdAt; + protected string _createdBy; protected EntityState _entityState; - private Guid? _id; - private DateTimeOffset? _modifiedAt; - private string? _modifiedBy; + protected Guid _id; + protected DateTimeOffset? _modifiedAt; + protected string? _modifiedBy; protected int _version; public EntityBase AsTrackable() @@ -99,7 +101,7 @@ public Dictionary GetOriginalValues() return this._originalValues; } - public DateTimeOffset? CreatedAt + public DateTimeOffset CreatedAt { get => this._createdAt; init => this._createdAt = value; @@ -129,22 +131,31 @@ public void ChangeTrack(string propertyName, object value) var changes = this._changedProperties; var originals = this._originalValues; + if (originals.Count <= 0) + { + throw new Exception("尚未啟用追蹤"); + } + if (changes.ContainsKey(propertyName) == false) { if (originals[propertyName] != value) { changes.Add(propertyName, value); - this._entityState = EntityState.Added; + this._entityState = EntityState.Modified; } } else { - if (originals[propertyName] != changes[propertyName]) + if (originals[propertyName].ToString() == value.ToString()) { - changes[propertyName] = value; - this._entityState = EntityState.Modified; + changes.Remove(propertyName); } } + + if (changes.Count <= 0) + { + this._entityState = EntityState.Unchanged; + } } private void Validate() diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs index a0f626c9..605ff333 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTime.cs @@ -4,9 +4,9 @@ namespace Lab.ChangeTracking.Domain; public interface IChangeTime { - DateTimeOffset? CreatedAt { get; init; } + DateTimeOffset CreatedAt { get; init; } - string? CreatedBy { get; init; } + string CreatedBy { get; init; } DateTimeOffset? ModifiedAt { get; init; } diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs deleted file mode 100644 index 26cfefd8..00000000 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/PropertyChangeTracker.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Lab.ChangeTracking.Domain; - -public class PropertyChangeTracker -{ - private readonly Dictionary _changedProperties = new(); - private readonly Dictionary _originalValues = new(); - - public Dictionary GetChangedProperties() - { - return this._changedProperties; - } - - public Dictionary GetOriginalValues() - { - throw new NotImplementedException(); - } - - public void Initial() - { - var properties = this.GetType().GetProperties(); - foreach (var property in properties) - { - this._originalValues.Add(property.Name, property.GetValue(this)); - } - } - - public void Track(string propertyName, object value) - { - var changes = this._changedProperties; - var originals = this._originalValues; - if (changes.ContainsKey(propertyName) == false) - { - changes.Add(propertyName, value); - } - else - { - if (originals[propertyName] != changes[propertyName]) - { - changes[propertyName] = value; - } - - if (originals[propertyName] == changes[propertyName]) - { - changes.Remove(propertyName); - } - } - } -} \ No newline at end of file From c9d8b65c268550868c8625f59cc91c8de52b2802 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 7 Mar 2022 01:15:12 +0800 Subject: [PATCH 175/267] =?UTF-8?q?=E6=A1=88=E4=BE=8B=E9=96=8B=E5=A7=8B?= =?UTF-8?q?=E5=89=8D=E5=BE=8C=E5=88=AA=E9=99=A4=E8=B3=87=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeTrackingUnitTest.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs index 345a17be..9942df35 100644 --- a/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs +++ b/Property Change Tracking/ChangeTracking2/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -5,6 +5,7 @@ using System.Text.Json; using System.Text.Unicode; using ChangeTracking; +using EFCore.BulkExtensions; using Lab.ChangeTracking.Infrastructure.DB.EntityModel; using Microsoft.EntityFrameworkCore; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -16,7 +17,7 @@ public class ChangeTrackingUnitTest { private readonly IEmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; - private readonly IDbContextFactory _employeeDbContextFactory = + private static readonly IDbContextFactory s_employeeDbContextFactory = TestAssistants.EmployeeDbContextFactory; private readonly IEmployeeRepository _employeeRepository = TestAssistants.EmployeeRepository; @@ -43,7 +44,7 @@ public void 異動追蹤後存檔() Remark = "我新的" }); var count = this._employeeRepository.SaveChangeAsync(trackable).Result; - var dbContext = this._employeeDbContextFactory.CreateDbContext(); + var dbContext = s_employeeDbContextFactory.CreateDbContext(); var actual = dbContext.Employees .Where(p => p.Id == employeeEntity.Id) .Include(p => p.Identity) @@ -93,7 +94,7 @@ public void 異動追蹤後存檔_回傳不可變的物件() private void DataShouldOk(EmployeeEntity source) { - var dbContext = this._employeeDbContextFactory.CreateDbContext(); + var dbContext = s_employeeDbContextFactory.CreateDbContext(); var actual = dbContext.Employees .Where(p => p.Id == source.Id) .Include(p => p.Identity) @@ -152,7 +153,24 @@ private static Employee Insert() dbContext.SaveChanges(); return toDB; } + [ClassCleanup] + public static void ClassCleanup() + { + DeleteAllTable(); + } + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + DeleteAllTable(); + } + private static void DeleteAllTable() + { + var dbContext = s_employeeDbContextFactory.CreateDbContext(); + dbContext.Employees.BatchDelete(); + dbContext.Addresses.BatchDelete(); + dbContext.Identity.BatchDelete(); + } private static string ToJson(T instance) { var serialize = JsonSerializer.Serialize(instance, From 165a9c2bf5e017eb45513a0db98ddba133386ed1 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 7 Mar 2022 01:30:54 +0800 Subject: [PATCH 176/267] refactor --- .../src/Lab.ChangeTracking.Domain/EntityState.cs | 3 +-- .../src/Lab.ChangeTracking.Domain/IChangeTrackable.cs | 4 ---- .../src/Lab.ChangeTracking.Domain/IChangeable.cs | 6 ------ 3 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs index ee413431..7fff1298 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityState.cs @@ -5,6 +5,5 @@ public enum EntityState Unchanged = 0, Added = 1, Modified = 2, - Deleted=3, - // Submitted = 99, + Deleted = 3, } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs index 02ceeab3..e891f5f9 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs @@ -14,8 +14,4 @@ public interface IChangeTrackable : IChangeTime, IChangeState Dictionary GetChangedProperties(); Dictionary GetOriginalValues(); - - // void SetTrackable(); - // - // void ChangeTrack(string propertyName, object value); } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs deleted file mode 100644 index 2c5125f8..00000000 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeable.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Lab.ChangeTracking.Domain; - -public interface IChangeable:IChangeTrackable -{ - -} \ No newline at end of file From 6117d954fe494e397d850d4d9766d03877adc88a Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 7 Mar 2022 16:19:15 +0800 Subject: [PATCH 177/267] refactor --- .../EmployeeAggregate/Entity/EmployeeEntity.cs | 2 +- .../src/Lab.ChangeTracking.Domain/EntityBase.cs | 2 +- .../src/Lab.ChangeTracking.Domain/IChangeContent.cs | 8 ++++++++ .../src/Lab.ChangeTracking.Domain/IChangeTrackable.cs | 8 ++------ 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeContent.cs diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs index d481562c..b40da0a2 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -76,7 +76,7 @@ public EmployeeEntity New(string name, int age, string remark = null) return this; } - public override void Reset() + public override void RejectChanges() { throw new NotImplementedException(); } diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs index d6477afb..f0f0dfdf 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/EntityBase.cs @@ -71,7 +71,7 @@ public EntityBase AsTrackable() return (null, true); } - public abstract void Reset(); + public abstract void RejectChanges(); public Dictionary GetChangedProperties() { diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeContent.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeContent.cs new file mode 100644 index 00000000..6d899ee9 --- /dev/null +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeContent.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeContent +{ + Dictionary GetChangedProperties(); + + Dictionary GetOriginalValues(); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs index e891f5f9..4c565a05 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs @@ -1,6 +1,6 @@ namespace Lab.ChangeTracking.Domain; -public interface IChangeTrackable : IChangeTime, IChangeState +public interface IChangeTrackable : IChangeContent, IChangeTime, IChangeState { // bool HasChanged { get; } public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, @@ -9,9 +9,5 @@ public interface IChangeTrackable : IChangeTime, IChangeState void ChangeTrack(string propertyName, object value); - void Reset(); - - Dictionary GetChangedProperties(); - - Dictionary GetOriginalValues(); + void RejectChanges(); } \ No newline at end of file From 6f54bd217b3300c4137162c362f57af75931d44e Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 8 Mar 2022 01:44:25 +0800 Subject: [PATCH 178/267] refactor --- .../src/Lab.ChangeTracking.Domain/CommitState.cs | 4 ++-- .../src/Lab.ChangeTracking.Domain/IChangeTrackable.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs index 0ba74bce..bc28b09f 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/CommitState.cs @@ -3,6 +3,6 @@ namespace Lab.ChangeTracking.Domain; public enum CommitState { Unchanged = 0, - Rejected = 1, - Accepted = 99, + Accepted = 1, + Rejected = 2, } \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs index 4c565a05..ae42e1f4 100644 --- a/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs +++ b/Property Change Tracking/ChangeTrackProperty/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs @@ -2,7 +2,6 @@ namespace Lab.ChangeTracking.Domain; public interface IChangeTrackable : IChangeContent, IChangeTime, IChangeState { - // bool HasChanged { get; } public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, IAccessContext accessContext, IUUIdProvider idProvider); From 6a94eb0a1747f8edaf129fd0be0af3e53803ac42 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 12:24:34 +0800 Subject: [PATCH 179/267] add swagger doc project --- .../Swagger/Lab.SwaggerDoc/Lab.SwaggerDoc.sln | 16 +++++ .../Controllers/WeatherForecastController.cs | 32 +++++++++ .../Lab.Swashbuckle.AspNetCore6.csproj | 15 +++++ .../Lab.Swashbuckle.AspNetCore6/Program.cs | 67 +++++++++++++++++++ .../Properties/launchSettings.json | 31 +++++++++ .../WeatherForecast.cs | 12 ++++ .../appsettings.Development.json | 8 +++ .../appsettings.json | 9 +++ 8 files changed, 190 insertions(+) create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.SwaggerDoc.sln create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.json diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.SwaggerDoc.sln b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.SwaggerDoc.sln new file mode 100644 index 00000000..59eaf419 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.SwaggerDoc.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Swashbuckle.AspNetCore6", "Lab.Swashbuckle.AspNetCore6\Lab.Swashbuckle.AspNetCore6.csproj", "{2DA608D5-BC95-4DF6-AB7A-0A22085E9257}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..539c6b34 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.Swashbuckle.AspNetCore6.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj new file mode 100644 index 00000000..257d3e70 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + bin\ + bin\Lab.Swashbuckle.AspNetCore6.xml + + + + + + + diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs new file mode 100644 index 00000000..f046f113 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -0,0 +1,67 @@ +using System.Reflection; +using System.Xml.XPath; +using Swashbuckle.AspNetCore.SwaggerGen; + +void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) +{ + var directory = AppDomain.CurrentDomain.BaseDirectory; + if (assembly != null) + { + foreach (var name in assembly.GetManifestResourceNames() + .Where(x => x.ToUpper() + .EndsWith(".XML")) + ) + { + try + { + var xPath = new XPathDocument(assembly.GetManifestResourceStream(name)); + swaggerGenOptions.IncludeXmlComments((Func)(() => xPath)); + } + catch + { + } + } + } + + if (string.IsNullOrEmpty(directory)) + { + return; + } + + foreach (var file in Directory.GetFiles(directory, "*.XML", SearchOption.AllDirectories)) + { + swaggerGenOptions.IncludeXmlComments(file); + } +} + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + var assembly = typeof(Program).Assembly; + + IncludeXmlComments(assembly, c); +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json new file mode 100644 index 00000000..a270a625 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51786", + "sslPort": 44377 + } + }, + "profiles": { + "Lab.Swashbuckle.AspNetCore6": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7236;http://localhost:5236", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs new file mode 100644 index 00000000..e896017e --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Lab.Swashbuckle.AspNetCore6; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.json b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From b763c2bba3f55cfec9ddc1a4476e6588c3a4afa8 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 17:40:40 +0800 Subject: [PATCH 180/267] refactor --- .../Controllers/EmployeeController.cs | 47 +++++++++++++++++++ .../Controllers/WeatherForecastController.cs | 32 ------------- .../EmployeeResponse.cs | 12 +++++ .../Lab.Swashbuckle.AspNetCore6/Program.cs | 26 ++++++++-- .../QueryEmployeeRequest.cs | 19 ++++++++ .../WeatherForecast.cs | 12 ----- 6 files changed, 100 insertions(+), 48 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs delete mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs delete mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs new file mode 100644 index 00000000..2e78a73f --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.Swashbuckle.AspNetCore6.Controllers; + +[ApiController] +[Route("[controller]")] +public class EmployeeController : ControllerBase +{ + private static readonly string[] Summaries = + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public EmployeeController(ILogger logger) + { + this._logger = logger; + } + + // [HttpGet(Name = "GetEmployee")] + [HttpGet] + public async Task Get(QueryEmployeeRequest request) + { + if (this.ModelState.IsValid == false) + { + return this.BadRequest(); + } + + return this.Ok(new List + { + new() + { + Id = Guid.NewGuid(), + Name = "yao", + Age = 20 + }, + new() + { + Id = Guid.NewGuid(), + Name = "小章", + Age = 18, + Remark = "說明" + } + }); + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs deleted file mode 100644 index 539c6b34..00000000 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Lab.Swashbuckle.AspNetCore6.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - this._logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs new file mode 100644 index 00000000..58a58d54 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs @@ -0,0 +1,12 @@ +namespace Lab.Swashbuckle.AspNetCore6; + +public class EmployeeResponse +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public int Age { get; set; } + + public string Remark { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs index f046f113..680dfdc1 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Xml.XPath; +using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) @@ -42,11 +43,28 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(c => +builder.Services.AddSwaggerGen(options => { - var assembly = typeof(Program).Assembly; - - IncludeXmlComments(assembly, c); + options.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "ToDo API", + Description = "An ASP.NET Core Web API for managing ToDo items", + TermsOfService = new Uri("https://example.com/terms"), + Contact = new OpenApiContact + { + Name = "Example Contact", + Url = new Uri("https://example.com/contact") + }, + License = new OpenApiLicense + { + Name = "Example License", + Url = new Uri("https://example.com/license") + } + }); + + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }); var app = builder.Build(); diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs new file mode 100644 index 00000000..c91fe73f --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace Lab.Swashbuckle.AspNetCore6; + +public class QueryEmployeeRequest +{ + /// + /// 姓名 + /// + /// 小章 + [Required] + public string Name { get; set; } + + /// + /// 年齡 + /// + /// 18 + public int Age { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs deleted file mode 100644 index e896017e..00000000 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Lab.Swashbuckle.AspNetCore6; - -public class WeatherForecast -{ - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); - - public string? Summary { get; set; } -} \ No newline at end of file From f7c6739caf51e6cbf296bfd964b4600547a9db71 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 21:37:50 +0800 Subject: [PATCH 181/267] add api info --- .../Controllers/EmployeeController.cs | 14 ++++++++++++++ .../Lab.Swashbuckle.AspNetCore6.csproj | 11 ++++++++++- .../Lab.Swashbuckle.AspNetCore6/Program.cs | 15 ++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs index 2e78a73f..7d75e956 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs @@ -18,6 +18,20 @@ public EmployeeController(ILogger logger) this._logger = logger; } + /// + /// 取得會員 + /// + /// + /// + /// Sample request: + /// + /// POST /Todo + /// { + /// "id": 1, + /// "name": "Item #1", + /// "isComplete": true + /// } + /// // [HttpGet(Name = "GetEmployee")] [HttpGet] public async Task Get(QueryEmployeeRequest request) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj index 257d3e70..4ad950f2 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj @@ -9,7 +9,16 @@ - + + + + + + + + + + diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs index 680dfdc1..87f39d14 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -48,8 +48,8 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", - Title = "ToDo API", - Description = "An ASP.NET Core Web API for managing ToDo items", + Title = "Employee API", + Description = "An ASP.NET Core Web API for managing employees", TermsOfService = new Uri("https://example.com/terms"), Contact = new OpenApiContact { @@ -62,6 +62,11 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) Url = new Uri("https://example.com/license") } }); + options.SwaggerDoc("v2", new OpenApiInfo + { + Version = "v2", + Title = "Employee API", + }); var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); @@ -73,7 +78,11 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(options=> + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2"); + }); } app.UseHttpsRedirection(); From 19ebd71111207c19c671b251cc0edab31bdeba87 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 22:43:48 +0800 Subject: [PATCH 182/267] add example --- .../Controllers/EmployeeController.cs | 9 +++++++++ .../EmployeeResponse.cs | 12 ++++++++++++ .../Examples/EmployeeResponseExample.cs | 17 +++++++++++++++++ .../Examples/QueryEmployeeRequestExample.cs | 15 +++++++++++++++ .../Lab.Swashbuckle.AspNetCore6.csproj | 11 ++--------- .../Lab.Swashbuckle.AspNetCore6/Program.cs | 5 ++++- 6 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/EmployeeResponseExample.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs index 7d75e956..8be2ffb6 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs @@ -1,4 +1,7 @@ +using Lab.Swashbuckle.AspNetCore6.Examples; using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.Filters; namespace Lab.Swashbuckle.AspNetCore6.Controllers; @@ -32,8 +35,14 @@ public EmployeeController(ILogger logger) /// "isComplete": true /// } /// + // [HttpGet(Name = "GetEmployee")] [HttpGet] + [Produces("application/json")] + // [ProducesResponseType(typeof(EmployeeResponse), StatusCodes.Status200OK)] + // [SwaggerResponse(200, "The list of countries", typeof(IEnumerable))] + [SwaggerRequestExample(typeof(QueryEmployeeRequest), typeof(QueryEmployeeRequestExample))] + [SwaggerResponseExample(200, typeof(EmployeeResponseExample))] public async Task Get(QueryEmployeeRequest request) { if (this.ModelState.IsValid == false) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs index 58a58d54..19f88949 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs @@ -2,11 +2,23 @@ namespace Lab.Swashbuckle.AspNetCore6; public class EmployeeResponse { + /// + /// 編號 + /// public Guid Id { get; set; } + /// + /// 姓名 + /// public string Name { get; set; } + /// + /// 年齡 + /// public int Age { get; set; } + /// + /// 註解 + /// public string Remark { get; set; } } \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/EmployeeResponseExample.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/EmployeeResponseExample.cs new file mode 100644 index 00000000..f0e56706 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/EmployeeResponseExample.cs @@ -0,0 +1,17 @@ +using Swashbuckle.AspNetCore.Filters; + +namespace Lab.Swashbuckle.AspNetCore6.Examples; + +public class EmployeeResponseExample : IExamplesProvider +{ + public EmployeeResponse GetExamples() + { + return new EmployeeResponse + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000001"), + Name = "小章", + Age = 18, + Remark = "說明" + }; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs new file mode 100644 index 00000000..51b1a09c --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs @@ -0,0 +1,15 @@ +using Swashbuckle.AspNetCore.Filters; + +namespace Lab.Swashbuckle.AspNetCore6.Examples; + +public class QueryEmployeeRequestExample : IExamplesProvider +{ + public QueryEmployeeRequest GetExamples() + { + return new QueryEmployeeRequest + { + Name = "小章", + Age = 18 + }; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj index 4ad950f2..3da1ae7f 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj @@ -10,15 +10,8 @@ - - - - - - - - - + + diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs index 87f39d14..8a2990d9 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Xml.XPath; using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.SwaggerGen; void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) @@ -67,10 +68,12 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) Version = "v2", Title = "Employee API", }); - + options.ExampleFilters(); + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }); +builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly()); var app = builder.Build(); From 5c627f5ec69afa78e5027c394da0555bbc968f0c Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 23:19:04 +0800 Subject: [PATCH 183/267] add JsonStringEnumMemberConverter --- .../Controllers/EmployeeController.cs | 2 +- .../EmployeeResponse.cs | 8 +++--- .../Examples/QueryEmployeeRequestExample.cs | 3 ++- .../Lab.Swashbuckle.AspNetCore6.csproj | 7 ++--- .../Lab.Swashbuckle.AspNetCore6/Program.cs | 10 +++---- .../QueryEmployeeRequest.cs | 27 +++++++++++++++++-- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs index 8be2ffb6..bee2d16e 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Controllers/EmployeeController.cs @@ -40,7 +40,7 @@ public EmployeeController(ILogger logger) [HttpGet] [Produces("application/json")] // [ProducesResponseType(typeof(EmployeeResponse), StatusCodes.Status200OK)] - // [SwaggerResponse(200, "The list of countries", typeof(IEnumerable))] + [SwaggerResponse(200, "查詢結果", typeof(EmployeeResponse))] [SwaggerRequestExample(typeof(QueryEmployeeRequest), typeof(QueryEmployeeRequestExample))] [SwaggerResponseExample(200, typeof(EmployeeResponseExample))] public async Task Get(QueryEmployeeRequest request) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs index 19f88949..99f842b9 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/EmployeeResponse.cs @@ -3,22 +3,22 @@ namespace Lab.Swashbuckle.AspNetCore6; public class EmployeeResponse { /// - /// 編號 + /// 編號 /// public Guid Id { get; set; } /// - /// 姓名 + /// 姓名 /// public string Name { get; set; } /// - /// 年齡 + /// 年齡 /// public int Age { get; set; } /// - /// 註解 + /// 註解 /// public string Remark { get; set; } } \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs index 51b1a09c..44932cf9 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Examples/QueryEmployeeRequestExample.cs @@ -9,7 +9,8 @@ public QueryEmployeeRequest GetExamples() return new QueryEmployeeRequest { Name = "小章", - Age = 18 + Age = 18, + // State = (State)1 }; } } \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj index 3da1ae7f..bc9b1642 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj @@ -9,9 +9,10 @@ - - - + + + + diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs index 8a2990d9..61e705bd 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -66,10 +66,10 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) options.SwaggerDoc("v2", new OpenApiInfo { Version = "v2", - Title = "Employee API", - }); + Title = "Employee API" + }); options.ExampleFilters(); - + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }); @@ -81,10 +81,10 @@ void IncludeXmlComments(Assembly assembly, SwaggerGenOptions swaggerGenOptions) if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(options=> + app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); - options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2"); + options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2"); }); } diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs index c91fe73f..c4cf8982 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs @@ -1,19 +1,42 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; namespace Lab.Swashbuckle.AspNetCore6; public class QueryEmployeeRequest { /// - /// 姓名 + /// 姓名 /// /// 小章 [Required] public string Name { get; set; } /// - /// 年齡 + /// 年齡 /// /// 18 public int Age { get; set; } + + /// + /// 狀態 + /// + /// 1 + public State State { get; set; } +} + +// [JsonConverter(typeof(JsonStringEnumMemberConverter))] // This custom converter was placed in a system namespace. +public enum State +{ + None = 0, + + /// + /// Approved + /// + Approved = 1, + + /// + /// Rejected + /// + Rejected = 2 } \ No newline at end of file From db68b2460da77b7e9df0c1f6b85409fa0ed1b4e8 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 12 Mar 2022 23:33:43 +0800 Subject: [PATCH 184/267] enum to string --- .../QueryEmployeeRequest.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs index c4cf8982..48f021d9 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc/Lab.Swashbuckle.AspNetCore6/QueryEmployeeRequest.cs @@ -1,4 +1,6 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; using System.Text.Json.Serialization; namespace Lab.Swashbuckle.AspNetCore6; @@ -25,18 +27,24 @@ public class QueryEmployeeRequest public State State { get; set; } } -// [JsonConverter(typeof(JsonStringEnumMemberConverter))] // This custom converter was placed in a system namespace. +[JsonConverter(typeof(JsonStringEnumMemberConverter))] // This custom converter was placed in a system namespace. public enum State { + [EnumMember(Value = "UNKNOWN_DEFINITION_000")] + None = 0, /// /// Approved /// + /// Approved + // [Description("Approved")] + [EnumMember(Value = "Approved")] Approved = 1, /// /// Rejected /// + [EnumMember(Value = "Rejected")] Rejected = 2 } \ No newline at end of file From 655b2fdbcdc892d5fb0dae70e135d1e962769f71 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 13 Mar 2022 18:53:45 +0800 Subject: [PATCH 185/267] feat: add initial project --- .../Lab.SwaggerDoc.MultiVersion.sln | 16 +++ .../Employee/v1_0/DemoController.cs | 19 ++++ .../Employee/v1_1/DemoController.cs | 19 ++++ .../Lab.Swashbuckle.AspNetCore6.csproj | 23 +++++ .../Lab.Swashbuckle.AspNetCore6/Program.cs | 97 +++++++++++++++++++ .../Properties/launchSettings.json | 31 ++++++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 ++ 8 files changed, 222 insertions(+) create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.json diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln new file mode 100644 index 00000000..59eaf419 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Swashbuckle.AspNetCore6", "Lab.Swashbuckle.AspNetCore6\Lab.Swashbuckle.AspNetCore6.csproj", "{2DA608D5-BC95-4DF6-AB7A-0A22085E9257}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs new file mode 100644 index 00000000..cf1b00bd --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_0; + +[ApiVersion("1.0", Deprecated = true)] +[ApiController] +[Route("api/[controller]")] +public class DemoController : ControllerBase +{ + [HttpGet] + public IActionResult Get() + { + return this.Ok(new + { + Version = 1.0, + Name = "1.0" + }); + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs new file mode 100644 index 00000000..9042a16a --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_1; + +[ApiVersion("1.1")] +[ApiController] +[Route("api/[controller]")] +public class DemoController : ControllerBase +{ + [HttpGet] + public IActionResult Get() + { + return this.Ok(new + { + Version = 1.1, + Name = "1.1" + }); + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj new file mode 100644 index 00000000..c2f22baf --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Lab.Swashbuckle.AspNetCore6.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + bin\ + bin\Lab.Swashbuckle.AspNetCore6.xml + + + + + + + + + + + + + + + diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs new file mode 100644 index 00000000..a4fa85c6 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -0,0 +1,97 @@ +using System.Reflection; +using System.Xml.XPath; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.Filters; +using Swashbuckle.AspNetCore.SwaggerGen; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "Employee API", + Description = "An ASP.NET Core Web API for managing employees", + TermsOfService = new Uri("https://example.com/terms"), + Contact = new OpenApiContact + { + Name = "Example Contact", + Url = new Uri("https://example.com/contact") + }, + License = new OpenApiLicense + { + Name = "Example License", + Url = new Uri("https://example.com/license") + } + }); + options.SwaggerDoc("v2", new OpenApiInfo + { + Version = "v2", + Title = "Employee API" + }); + options.ExampleFilters(); + + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); +}); +builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly()); +builder.Services.AddApiVersioning(option => +{ + //返回響應標頭中支援的版本資訊 + option.ReportApiVersions = true; + + //未提供版本請請時,使用預設版號 + option.AssumeDefaultVersionWhenUnspecified = true; + + //預設api版本號,支援時間或數字版本號 + option.DefaultApiVersion = new ApiVersion(1, 0); + + //支援MediaType、Header、QueryString 設定版本號;預設為 QueryString、UrlSegment + option.ApiVersionReader = ApiVersionReader.Combine( + new MediaTypeApiVersionReader("api-version"), + new HeaderApiVersionReader("api-version"), + new QueryStringApiVersionReader("api-version"), + new UrlSegmentApiVersionReader()); +}); + +// builder.Services.AddVersionedApiExplorer(options => +// { +// // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service +// // note: the specified format code will format the version as "'v'major[.minor][-status]" +// options.GroupNameFormat = "'v'VVV"; +// +// // note: this option is only necessary when versioning by url segment. the SubstitutionFormat +// // can also be used to control the format of the API version in route templates +// options.SubstituteApiVersionInUrl = true; +// }); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2"); + }); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.UseApiVersioning(); + +app.Run(); \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json new file mode 100644 index 00000000..a270a625 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51786", + "sslPort": 44377 + } + }, + "profiles": { + "Lab.Swashbuckle.AspNetCore6": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7236;http://localhost:5236", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.json b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From 78ac76b6e1ab9a686414d439f450b6d23e5a43b2 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 13 Mar 2022 20:29:33 +0800 Subject: [PATCH 186/267] feat: set UrlSegmentApiVersionReader --- .../Controllers/Employee/v1_0/DemoController.cs | 1 + .../Controllers/Employee/v1_1/DemoController.cs | 1 + .../Lab.Swashbuckle.AspNetCore6/Program.cs | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs index cf1b00bd..2bd788c8 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs @@ -5,6 +5,7 @@ namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_0; [ApiVersion("1.0", Deprecated = true)] [ApiController] [Route("api/[controller]")] +[Route("api/v{version:apiVersion}/[controller]")] public class DemoController : ControllerBase { [HttpGet] diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs index 9042a16a..e46c81e4 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs @@ -5,6 +5,7 @@ namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_1; [ApiVersion("1.1")] [ApiController] [Route("api/[controller]")] +[Route("api/v{version:apiVersion}/[controller]")] public class DemoController : ControllerBase { [HttpGet] diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs index a4fa85c6..559b4593 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -50,12 +50,12 @@ option.ReportApiVersions = true; //未提供版本請請時,使用預設版號 - option.AssumeDefaultVersionWhenUnspecified = true; + option.AssumeDefaultVersionWhenUnspecified = false; //預設api版本號,支援時間或數字版本號 option.DefaultApiVersion = new ApiVersion(1, 0); - //支援MediaType、Header、QueryString 設定版本號;預設為 QueryString、UrlSegment + //支援MediaType、Header、QueryString 設定版本號 option.ApiVersionReader = ApiVersionReader.Combine( new MediaTypeApiVersionReader("api-version"), new HeaderApiVersionReader("api-version"), From 84cbfe2af6ad9314c97b1495c538183d009629b8 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 13 Mar 2022 21:28:00 +0800 Subject: [PATCH 187/267] =?UTF-8?q?feat:=20Swagger=20UI=20=E5=A5=97?= =?UTF-8?q?=E7=94=A8=E5=A4=9A=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.SwaggerDoc.MultiVersion.sln | 6 ++ .../ConfigureApiVersionSwaggerGenOptions.cs | 55 +++++++++++++++ .../Employee/v1_0/DemoController.cs | 2 +- .../Employee/v1_1/DemoController.cs | 2 +- .../Lab.Swashbuckle.AspNetCore6/Program.cs | 67 +++++++++---------- 5 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/ConfigureApiVersionSwaggerGenOptions.cs diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln index 59eaf419..db633377 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.SwaggerDoc.MultiVersion.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Swashbuckle.AspNetCore6", "Lab.Swashbuckle.AspNetCore6\Lab.Swashbuckle.AspNetCore6.csproj", "{2DA608D5-BC95-4DF6-AB7A-0A22085E9257}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiVersioningSample", "..\..\..\..\lab\blogsamples\ApiVersioningSample\ApiVersioningSample.csproj", "{D3DAD3B2-CA83-40E0-90B7-BF005C7C7210}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Debug|Any CPU.Build.0 = Debug|Any CPU {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.ActiveCfg = Release|Any CPU {2DA608D5-BC95-4DF6-AB7A-0A22085E9257}.Release|Any CPU.Build.0 = Release|Any CPU + {D3DAD3B2-CA83-40E0-90B7-BF005C7C7210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3DAD3B2-CA83-40E0-90B7-BF005C7C7210}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3DAD3B2-CA83-40E0-90B7-BF005C7C7210}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3DAD3B2-CA83-40E0-90B7-BF005C7C7210}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/ConfigureApiVersionSwaggerGenOptions.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/ConfigureApiVersionSwaggerGenOptions.cs new file mode 100644 index 00000000..a401bd5f --- /dev/null +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/ConfigureApiVersionSwaggerGenOptions.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Lab.Swashbuckle.AspNetCore6; + +public class ConfigureApiVersionSwaggerGenOptions : IConfigureOptions +{ + private readonly IApiVersionDescriptionProvider _provider; + + public ConfigureApiVersionSwaggerGenOptions(IApiVersionDescriptionProvider provider) + { + _provider = provider; + } + + public void Configure(SwaggerGenOptions options) + { + foreach (var description in _provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + } + + private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + { + //產生 API 資訊 + var info = new OpenApiInfo + { + Version = description.ApiVersion.ToString(), + Title = "Employee API", + Description = + @"

Sample API with versioning including Swagger.

Partly taken from this repository.

", + TermsOfService = new Uri("https://example.com/terms"), + Contact = new OpenApiContact + { + Name = "Example Contact", + Url = new Uri("https://example.com/contact") + }, + License = new OpenApiLicense + { + Name = "Example License", + Url = new Uri("https://example.com/license") + } + }; + + if (description.IsDeprecated) + { + info.Description += + @"

VERSION IS DEPRECATED

"; + } + + return info; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs index 2bd788c8..8c32b471 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_0/DemoController.cs @@ -4,7 +4,7 @@ namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_0; [ApiVersion("1.0", Deprecated = true)] [ApiController] -[Route("api/[controller]")] +// [Route("api/[controller]")] [Route("api/v{version:apiVersion}/[controller]")] public class DemoController : ControllerBase { diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs index e46c81e4..7c29fdeb 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Controllers/Employee/v1_1/DemoController.cs @@ -4,7 +4,7 @@ namespace Lab.Swashbuckle.AspNetCore6.Controllers.Employee.v1_1; [ApiVersion("1.1")] [ApiController] -[Route("api/[controller]")] +// [Route("api/[controller]")] [Route("api/v{version:apiVersion}/[controller]")] public class DemoController : ControllerBase { diff --git a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs index 559b4593..8158e877 100644 --- a/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs +++ b/WebAPI/Swagger/Lab.SwaggerDoc.MultiVersion/Lab.Swashbuckle.AspNetCore6/Program.cs @@ -1,7 +1,10 @@ using System.Reflection; using System.Xml.XPath; +using Lab.Swashbuckle.AspNetCore6; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.SwaggerGen; @@ -16,28 +19,6 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { - options.SwaggerDoc("v1", new OpenApiInfo - { - Version = "v1", - Title = "Employee API", - Description = "An ASP.NET Core Web API for managing employees", - TermsOfService = new Uri("https://example.com/terms"), - Contact = new OpenApiContact - { - Name = "Example Contact", - Url = new Uri("https://example.com/contact") - }, - License = new OpenApiLicense - { - Name = "Example License", - Url = new Uri("https://example.com/license") - } - }); - options.SwaggerDoc("v2", new OpenApiInfo - { - Version = "v2", - Title = "Employee API" - }); options.ExampleFilters(); var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; @@ -63,27 +44,39 @@ new UrlSegmentApiVersionReader()); }); -// builder.Services.AddVersionedApiExplorer(options => -// { -// // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service -// // note: the specified format code will format the version as "'v'major[.minor][-status]" -// options.GroupNameFormat = "'v'VVV"; -// -// // note: this option is only necessary when versioning by url segment. the SubstitutionFormat -// // can also be used to control the format of the API version in route templates -// options.SubstituteApiVersionInUrl = true; -// }); +builder.Services.AddVersionedApiExplorer(options => +{ + + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + //options.GroupNameFormat = "'v'VVV"; + + // note: this option is only necessary when versioning by url segment. the SubstitutionFormat + // can also be used to control the format of the API version in route templates + //options.SubstituteApiVersionInUrl = true; +}); +builder.Services.AddSingleton, ConfigureApiVersionSwaggerGenOptions>(); + var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); - options.SwaggerEndpoint("/swagger/v2/swagger.json", "v2"); - }); + + app.UseSwaggerUI( + options => + { + var provider = app.Services.GetService(); + + // build a swagger endpoint for each discovered API version + foreach (var description in provider.ApiVersionDescriptions) + { + var url = $"/swagger/{description.GroupName}/swagger.json"; + options.SwaggerEndpoint(url, + description.GroupName.ToUpperInvariant()); + } + }); } app.UseHttpsRedirection(); From 5d9e8a45c8ad0bfe77e766d8e6fa1232b84db500 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 23 Mar 2022 00:13:35 +0800 Subject: [PATCH 188/267] first project --- .../ChangeTrackVersion/ChangeTrackVersion.sln | 41 ++++ .../ChangeTrackVersion/Makefile | 4 + .../ChangeTrackVersion/docker-compose.yml | 10 + .../ChangeTrackingUnitTest.cs | 190 ++++++++++++++++++ .../Lab.ChangeTracking.Domain.UnitTest.csproj | 28 +++ .../MsTestHook.cs | 31 +++ .../TestAssistants.cs | 59 ++++++ .../AccessContext.cs | 14 ++ .../Lab.ChangeTracking.Domain/ChangeState.cs | 8 + .../Lab.ChangeTracking.Domain/CommitState.cs | 9 + .../EmployeeAggregate/EmployeeAggregate.cs | 187 +++++++++++++++++ .../EmployeeAggregate/Entity/AddressEntity.cs | 24 +++ .../Entity/EmployeeEntity.cs | 30 +++ .../Entity/IdentityEntity.cs | 20 ++ .../EmployeeAggregate/IEmployeeAggregate.cs | 1 + .../Repository/EmployeeRepository.cs | 41 ++++ .../Repository/IEmployeeRepository.cs | 8 + .../Repository/TypeConverterExtensions.cs | 145 +++++++++++++ .../Lab.ChangeTracking.Domain/EntityBase.cs | 140 +++++++++++++ .../src/Lab.ChangeTracking.Domain/Error.cs | 3 + .../IChangeContent.cs | 8 + .../Lab.ChangeTracking.Domain/IChangeState.cs | 10 + .../Lab.ChangeTracking.Domain/IChangeTime.cs | 16 ++ .../IChangeTrackable.cs | 12 ++ .../Lab.ChangeTracking.Domain/ISystemClock.cs | 14 ++ .../IUUIdProvider.cs | 14 ++ .../Lab.ChangeTracking.Domain.csproj | 17 ++ .../TypeConverterExtension.cs | 114 +++++++++++ .../AppDependencyInjectionExtensions.cs | 48 +++++ .../AppEnvironmentOption.cs | 32 +++ .../EntityModel/Address.cs | 29 +++ .../EntityModel/Employee.cs | 34 ++++ .../EntityModel/EmployeeDbContext.cs | 120 +++++++++++ .../EntityModel/Identity.cs | 32 +++ .../EnvironmentAssistant.cs | 15 ++ ...ab.ChangeTracking.Infrastructure.DB.csproj | 16 ++ 36 files changed, 1524 insertions(+) create mode 100644 Property Change Tracking/ChangeTrackVersion/ChangeTrackVersion.sln create mode 100644 Property Change Tracking/ChangeTrackVersion/Makefile create mode 100644 Property Change Tracking/ChangeTrackVersion/docker-compose.yml create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/AccessContext.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ChangeState.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/CommitState.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EntityBase.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Error.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeContent.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeState.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTime.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ISystemClock.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/TypeConverterExtension.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs create mode 100644 Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj diff --git a/Property Change Tracking/ChangeTrackVersion/ChangeTrackVersion.sln b/Property Change Tracking/ChangeTrackVersion/ChangeTrackVersion.sln new file mode 100644 index 00000000..1c868fe3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/ChangeTrackVersion.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1F776B7D-C182-4DC3-BA57-844657BD9AC0}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + Makefile = Makefile + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{483A9EB7-C4AF-4D68-80ED-49277985C760}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Infrastructure.DB", "src\Lab.ChangeTracking.Infrastructure.DB\Lab.ChangeTracking.Infrastructure.DB.csproj", "{A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain", "src\Lab.ChangeTracking.Domain\Lab.ChangeTracking.Domain.csproj", "{A1C827F2-683E-470C-A3DE-BD6DD2BE5198}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ChangeTracking.Domain.UnitTest", "src\Lab.ChangeTracking.Domain.UnitTest\Lab.ChangeTracking.Domain.UnitTest.csproj", "{7066EE2C-28A8-4408-B212-E582608AB967}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C}.Release|Any CPU.Build.0 = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198}.Release|Any CPU.Build.0 = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7066EE2C-28A8-4408-B212-E582608AB967}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A2FDDEA2-F3BD-43B2-96FE-B12EA5BE909C} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {A1C827F2-683E-470C-A3DE-BD6DD2BE5198} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + {7066EE2C-28A8-4408-B212-E582608AB967} = {483A9EB7-C4AF-4D68-80ED-49277985C760} + EndGlobalSection +EndGlobal diff --git a/Property Change Tracking/ChangeTrackVersion/Makefile b/Property Change Tracking/ChangeTrackVersion/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/docker-compose.yml b/Property Change Tracking/ChangeTrackVersion/docker-compose.yml new file mode 100644 index 00000000..8ecb1567 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + db-sql: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=pass@w0rd1~ + ports: + - 1433:1433 \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs new file mode 100644 index 00000000..8c989b04 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/ChangeTrackingUnitTest.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using EFCore.BulkExtensions; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Lab.MultiTestCase.UnitTest; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ChangeTracking.Domain.UnitTest; + +[TestClass] +public class ChangeTrackingUnitTest +{ + private static readonly IAccessContext _accessContext = TestAssistants.AccessContext; + + private static readonly IUUIdProvider _uuIdProvider = TestAssistants.UUIdProvider; + + private static readonly IDbContextFactory s_employeeDbContextFactory = + TestAssistants.EmployeeDbContextFactory; + + private readonly EmployeeAggregate _employeeAggregate = TestAssistants.EmployeeAggregate; + + private readonly IEmployeeRepository _employeeRepository = TestAssistants.EmployeeRepository; + + private readonly ISystemClock _systemClock = TestAssistants.SystemClock; + + [ClassCleanup] + public static void ClassCleanup() + { + DeleteAllTable(); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + DeleteAllTable(); + } + + [TestMethod] + public void 刪除一筆資料() + { + // var fromDb = Insert(); + // var employeeEntity = new EmployeeValueObject(); + // employeeEntity.AsTrackable(fromDb) + // .SetDelete() + // .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + // + // var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + // + // Assert.AreEqual(1, count); + // var dbContext = s_employeeDbContextFactory.CreateDbContext(); + // + // var actual = dbContext.Employees + // .Where(p => p.Id == fromDb.Id) + // .Include(p => p.Identity) + // .Include(p => p.Addresses) + // .FirstOrDefault() + // ; + // Assert.AreEqual(null, actual); + } + + [TestMethod] + public void 更新一筆資料() + { + var fromDb = Insert(); + var target = this._employeeAggregate; + target.SetEntity(fromDb.To()) + .SetProfile("小章", 28); + target.AcceptChanges(); + var result = target.CommitChangeAsync().Result; + } + + [TestMethod] + public void 沒有異動() + { + // var fromDb = Insert(); + // var employeeEntity = new EmployeeValueObject(); + // employeeEntity.AsTrackable(fromDb) + // .SetProfile("小章", 19, "新來的") + // .SetProfile("yao", 18, "編輯") + // .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + // + // var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + // Assert.AreEqual(0, count); + } + + [TestMethod] + public void 新增() + { + var target = this._employeeAggregate; + target.NewEmployee("yao", 18); + target.AcceptChanges(); + var result = target.CommitChangeAsync().Result; + } + + [TestMethod] + public void 新增一筆資料() + { + // var employeeEntity = new EmployeeValueObject(); + // employeeEntity.New("yao", 10, "新的") + // .AcceptChanges(this._systemClock, _accessContext, _uuIdProvider); + // + // var count = this._employeeRepository.SaveChangeAsync(employeeEntity).Result; + // + // Assert.AreEqual(1, count); + // var dbContext = s_employeeDbContextFactory.CreateDbContext(); + // + // var actual = dbContext.Employees + // .Where(p => p.Id == employeeEntity.Id) + // .Include(p => p.Identity) + // .Include(p => p.Addresses) + // .First() + // ; + // Assert.AreEqual("yao", actual.Name); + // Assert.AreEqual(10, actual.Age); + // Assert.AreEqual("新的", actual.Remark); + } + + private static void DeleteAllTable() + { + var dbContext = s_employeeDbContextFactory.CreateDbContext(); + dbContext.Employees.BatchDelete(); + dbContext.Addresses.BatchDelete(); + dbContext.Identity.BatchDelete(); + } + + private static Employee Insert() + { + using var dbContext = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + var employeeId = Guid.NewGuid(); + var toDB = new Employee + { + Id = employeeId, + Age = 18, + Name = "yao", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", + Remark = "編輯", + Identity = new Identity + { + Employee_Id = employeeId, + Account = "yao", + Password = "123456", + CreatedAt = DateTimeOffset.Now, + CreatedBy = "Sys", + Remark = "編輯" + }, + Addresses = new List
+ { + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "修改的" + }, + new() + { + Id = Guid.NewGuid(), + Employee_Id = employeeId, + CreatedAt = DateTimeOffset.Now, + CreatedBy = "sys", + Country = "Taipei", + Street = "Street", + Remark = "刪除的" + } + } + }; + dbContext.Employees.Add(toDB); + dbContext.SaveChanges(); + return toDB; + } + + private static string ToJson(T instance) + { + var serialize = JsonSerializer.Serialize(instance, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create( + UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) + }); + return serialize; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj new file mode 100644 index 00000000..fbfda1f2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/Lab.ChangeTracking.Domain.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs new file mode 100644 index 00000000..ca75b47b --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/MsTestHook.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.MultiTestCase.UnitTest; + +[TestClass] +public class MsTestHook +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistants.SetTestEnvironmentVariable(); + var db = TestAssistants.EmployeeDbContextFactory.CreateDbContext(); + if (db.Database.CanConnect()) + { + db.Database.EnsureDeleted(); + } + + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs new file mode 100644 index 00000000..1fb69070 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain.UnitTest/TestAssistants.cs @@ -0,0 +1,59 @@ +using System; +using Lab.ChangeTracking.Domain; +using Lab.ChangeTracking.Infrastructure.DB; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.MultiTestCase.UnitTest; + +// assistant +internal class TestAssistants +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + public static IEmployeeRepository EmployeeRepository => + _serviceProvider.GetService(); + + public static EmployeeAggregate EmployeeAggregate => + _serviceProvider.GetService(); + + public static ISystemClock SystemClock => + _serviceProvider.GetService(); + + public static IAccessContext AccessContext => + _serviceProvider.GetService(); + + public static IUUIdProvider UUIdProvider => + _serviceProvider.GetService(); + + static TestAssistants() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + SetTestEnvironmentVariable(); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = + "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/AccessContext.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/AccessContext.cs new file mode 100644 index 00000000..85ba24fb --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/AccessContext.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IAccessContext +{ + public string? GetUserId(); +} + +public class AccessContext : IAccessContext +{ + public string? GetUserId() + { + return "Sys"; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ChangeState.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ChangeState.cs new file mode 100644 index 00000000..34f040f5 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ChangeState.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public enum ChangeState +{ + Added = 0, + Modified = 1, + Deleted = 2, +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/CommitState.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/CommitState.cs new file mode 100644 index 00000000..650245f3 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/CommitState.cs @@ -0,0 +1,9 @@ +namespace Lab.ChangeTracking.Domain; + +public enum CommitState +{ + Unchanged = 0, + Accepted = 1, + Rejected = 2, + Commited = 3 +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs new file mode 100644 index 00000000..e72ad508 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/EmployeeAggregate.cs @@ -0,0 +1,187 @@ +using ChangeTracking; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeAggregate +{ + public CommitState CommitState { get; private set; } + + private readonly IAccessContext _accessContext; + private readonly IUUIdProvider _idProvider; + + private readonly EmployeeRepository _repository; + private readonly ISystemClock _systemClock; + + private EmployeeEntity _instance; + + public EmployeeAggregate(IUUIdProvider idProvider, + ISystemClock systemClock, + IAccessContext accessContext, + EmployeeRepository repository) + { + this._idProvider = idProvider; + this._systemClock = systemClock; + this._accessContext = accessContext; + this._repository = repository; + } + + public EmployeeAggregate AcceptChanges() + { + if (this.CommitState == CommitState.Accepted) + { + throw new Exception("已接受核准"); + } + + this.CommitState = CommitState.Accepted; + var trackable = this._instance.CastToIChangeTrackable(); + if (trackable.IsChanged) + { + this._instance.Version++; + } + else + { + this._instance.Version = 1; + } + + return this; + } + + public EmployeeAggregate AddAddress(AddressEntity instance) + { + this.ValidateAcceptedState(); + var (when, who) = (this._systemClock.GetNow(), this._accessContext.GetUserId()); + instance.Id = this._idProvider.GenerateId(); + instance.CreatedAt = when; + instance.CreatedBy = who; + + this._instance.Addresses.Add(instance); + return this; + } + + public async Task CommitChangeAsync(CancellationToken cancel = default) + { + if (this.CommitState != CommitState.Accepted) + { + throw new Exception("未被認可"); + } + + var changeCount = 0; + switch (this._instance.EntityState) + { + case ChangeState.Added: + changeCount = await this._repository.InsertEmployeeAsync(this._instance, cancel); + + break; + case ChangeState.Modified: + changeCount = await this._repository.SaveChangesAsync(this._instance, cancel); + + break; + case ChangeState.Deleted: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + this.CommitState = CommitState.Unchanged; + return changeCount; + } + + public Guid GetEmployeeId() + { + return this._instance.Id; + } + + public EmployeeAggregate NewAddress(Guid employeeId, AddressEntity source) + { + this.CommitState = CommitState.Unchanged; + + var target = this._instance + .Addresses + .Where(p => p.Employee_Id == source.Employee_Id) + .FirstOrDefault(); + + if (target == null) + { + return this; + } + + var (when, who) = (this._systemClock.GetNow(), this._accessContext.GetUserId()); + + target.Id = source.Id; + target.Country = source.Country; + target.Street = source.Street; + target.Remark = source.Remark; + target.EntityState = ChangeState.Added; + target.ModifiedBy = who; + target.ModifiedAt = when; + return this; + } + + public Guid NewEmployee(string name, + int age, + string remark = null) + { + this.CommitState = CommitState.Unchanged; + + var (when, who) = (this._systemClock.GetNow(), this._accessContext.GetUserId()); + + this._instance = new EmployeeEntity + { + Id = this._idProvider.GenerateId(), + EntityState = ChangeState.Added, + Name = name, + Age = age, + Remark = remark, + CreatedAt = when, + CreatedBy = who, + ModifiedAt = when, + ModifiedBy = who, + }.AsTrackable(); + + return this._instance.Id; + } + + public EmployeeAggregate SetEntity(EmployeeEntity instance) + { + this.CommitState = CommitState.Unchanged; + + this._instance = instance.AsTrackable(); + this._instance.EntityState = ChangeState.Modified; + + return this; + } + + public void SetProfile(string name, + int age, + string remark = null) + { + this.ValidateAcceptedState(); + this.ValidateAddState(); + var (when, who) = + (this._systemClock.GetNow(), this._accessContext.GetUserId()); + this._instance.ModifiedBy = who; + this._instance.ModifiedAt = when; + + this._instance.Age = age; + this._instance.Name = name; + this._instance.Remark = remark; + } + + private void ValidateAcceptedState() + { + if (this.CommitState == CommitState.Accepted) + { + throw new Exception("已接受核准,不能改變狀態"); + } + + this.ValidateAddState(); + } + + private void ValidateAddState() + { + if (this._instance.EntityState == ChangeState.Added) + { + throw new Exception("Add 狀態不能異動"); + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs new file mode 100644 index 00000000..0bdc4ad2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/AddressEntity.cs @@ -0,0 +1,24 @@ +namespace Lab.ChangeTracking.Domain; + +public record AddressEntity +{ + public virtual Guid Id { get; set; } + + public virtual Guid Employee_Id { get; set; } + + public virtual ChangeState EntityState { get; set; } + + public virtual string Country { get; set; } + + public virtual string Street { get; set; } + + public virtual DateTimeOffset CreatedAt { get; set; } + + public virtual string CreatedBy { get; set; } + + public virtual DateTimeOffset? ModifiedAt { get; set; } + + public virtual string ModifiedBy { get; set; } + + public virtual string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs new file mode 100644 index 00000000..0bafc2b1 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/EmployeeEntity.cs @@ -0,0 +1,30 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain; + +public record EmployeeEntity +{ + public virtual Guid Id { get; set; } + + public virtual ChangeState EntityState { get; set; } + + public virtual string Name { get; set; } + + public virtual int? Age { get; set; } + + public virtual string Remark { get; set; } + + public virtual List Addresses { get; set; } = new(); + + public virtual IdentityEntity Identity { get; set; } + + public virtual string ModifiedBy { get; set; } + + public virtual DateTimeOffset? ModifiedAt { get; set; } + + public virtual DateTimeOffset CreatedAt { get; set; } + + public virtual string CreatedBy { get; set; } + + public virtual int Version { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs new file mode 100644 index 00000000..bcdde865 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Entity/IdentityEntity.cs @@ -0,0 +1,20 @@ +namespace Lab.ChangeTracking.Domain; + +public record IdentityEntity +{ + public Guid Employee_Id { get; set; } + + public string Account { get; set; } + + public string Password { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string? ModifiedBy { get; set; } + + public virtual string Remark { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs new file mode 100644 index 00000000..e251d05f --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/IEmployeeAggregate.cs @@ -0,0 +1 @@ +namespace Lab.ChangeTracking.Domain; diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs new file mode 100644 index 00000000..b0478a4f --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/EmployeeRepository.cs @@ -0,0 +1,41 @@ +using ChangeTracking; +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; + +namespace Lab.ChangeTracking.Domain; + +public class EmployeeRepository +{ + private readonly IDbContextFactory _employeeDbContextFactory; + + public EmployeeRepository(IDbContextFactory memberContextFactory) + { + this._employeeDbContextFactory = memberContextFactory; + } + + public async Task AddAsync(EmployeeEntity source, + CancellationToken cancel = default) + { + var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + var srcEmployee = source.CastToIChangeTrackable(); + + return await dbContext.SaveChangesAsync(cancel); + } + + public async Task SaveChangesAsync(EmployeeEntity source, + CancellationToken cancel = default) + { + var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + var employeeTrackable = source.CastToIChangeTrackable(); + + return await dbContext.SaveChangesAsync(cancel); + } + + public async Task InsertEmployeeAsync(EmployeeEntity srcEmployee, CancellationToken cancel = default) + { + var destEmployee = srcEmployee.To(); + var dbContext = await this._employeeDbContextFactory.CreateDbContextAsync(cancel); + await dbContext.AddAsync(destEmployee, cancel); + return await dbContext.SaveChangesAsync(cancel); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs new file mode 100644 index 00000000..0035d132 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/IEmployeeRepository.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IEmployeeRepository +{ + Task SaveChangeAsync(EmployeeEntity employee, + IEnumerable excludeProperties = null, + CancellationToken cancel = default); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs new file mode 100644 index 00000000..0b1e88be --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EmployeeAggregate/Repository/TypeConverterExtensions.cs @@ -0,0 +1,145 @@ +// using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +// +// namespace Lab.ChangeTracking.Domain; +// +// public static class TypeConverterExtensions +// { +// public static Employee To(this EmployeeValueObject srcEmployee) +// { +// if (srcEmployee == null) +// { +// return null; +// } +// +// return new Employee +// { +// Id = srcEmployee.Id, +// Name = srcEmployee.Name, +// Age = srcEmployee.Age, +// Version = srcEmployee.Version, +// Remark = srcEmployee.Remark, +// Addresses = srcEmployee.Addresses != null ? srcEmployee.Addresses.To().ToList() : null, +// Identity = srcEmployee.Identity.To(), +// CreatedAt = srcEmployee.CreatedAt, +// CreatedBy = srcEmployee.CreatedBy, +// ModifiedAt = srcEmployee.ModifiedAt, +// ModifiedBy = srcEmployee.ModifiedBy +// }; +// } +// +// public static EmployeeValueObject To(this Employee srcEmployee) +// { +// if (srcEmployee == null) +// { +// return null; +// } +// +// return new EmployeeValueObject +// { +// Id = srcEmployee.Id, +// Name = srcEmployee.Name, +// Age = srcEmployee.Age, +// Version = srcEmployee.Version, +// Remark = srcEmployee.Remark, +// Addresses = srcEmployee.Addresses.To()?.ToList(), +// Identity = srcEmployee.Identity.To(), +// CreatedAt = srcEmployee.CreatedAt, +// CreatedBy = srcEmployee.CreatedBy, +// ModifiedAt = srcEmployee.ModifiedAt, +// ModifiedBy = srcEmployee.ModifiedBy +// }; +// } +// +// public static Identity To(this IdentityEntity srcIdentity) +// { +// if (srcIdentity == null) +// { +// return null; +// } +// +// return new Identity +// { +// Employee_Id = srcIdentity.Employee_Id, +// Account = srcIdentity.Account, +// Password = srcIdentity.Password, +// Remark = srcIdentity.Remark, +// CreatedAt = srcIdentity.CreatedAt, +// CreatedBy = srcIdentity.CreatedBy, +// ModifiedAt = srcIdentity.ModifiedAt, +// ModifiedBy = srcIdentity.ModifiedBy +// }; +// } +// +// public static IdentityEntity To(this Identity srcIdentity) +// { +// if (srcIdentity == null) +// { +// return null; +// } +// +// return new IdentityEntity +// { +// Employee_Id = srcIdentity.Employee_Id, +// Account = srcIdentity.Account, +// Password = srcIdentity.Password, +// Remark = srcIdentity.Remark, +// CreatedAt = srcIdentity.CreatedAt, +// CreatedBy = srcIdentity.CreatedBy, +// ModifiedAt = srcIdentity.ModifiedAt, +// ModifiedBy = srcIdentity.ModifiedBy +// }; +// } +// +// public static Address To(this AddressEntity srcAddress) +// { +// if (srcAddress == null) +// { +// return null; +// } +// +// return new Address +// { +// Id = srcAddress.Id, +// Employee_Id = srcAddress.Employee_Id, +// Country = srcAddress.Country, +// Street = srcAddress.Street, +// CreatedAt = srcAddress.CreatedAt, +// CreatedBy = srcAddress.CreatedBy, +// ModifiedAt = srcAddress.ModifiedAt, +// ModifiedBy = srcAddress.ModifiedBy, +// Remark = srcAddress.Remark +// }; +// } +// +// public static AddressEntity To(this Address srcAddress) +// { +// if (srcAddress == null) +// { +// return null; +// } +// +// return new AddressEntity +// { +// Id = srcAddress.Id, +// Employee_Id = srcAddress.Employee_Id, +// Country = srcAddress.Country, +// Street = srcAddress.Street, +// CreatedAt = srcAddress.CreatedAt, +// CreatedBy = srcAddress.CreatedBy, +// ModifiedAt = srcAddress.ModifiedAt, +// ModifiedBy = srcAddress.ModifiedBy, +// Remark = srcAddress.Remark +// }; +// } +// +// public static IEnumerable
To(this IEnumerable srcProfiles) +// { +// return srcProfiles?.Select(p => p?.To()); +// } +// +// public static IEnumerable To(this IEnumerable
srcProfiles) +// { +// return srcProfiles?.Select(p => p?.To()); +// } +// } + diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EntityBase.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EntityBase.cs new file mode 100644 index 00000000..96bae77a --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/EntityBase.cs @@ -0,0 +1,140 @@ +using System.Collections; + +namespace Lab.ChangeTracking.Domain; + +public abstract record EntityBase : IChangeTrackable +{ + // public Guid Id + // { + // get => this._id; + // init => this._id = value; + // } + + protected readonly Dictionary _changedProperties = new(); + protected readonly Dictionary _originalValues = new(); + protected CommitState _commitState; + protected ChangeState _entityState; + protected Guid _id; + + public EntityBase AsTrackable() + { + this.Validate(); + + // this._entityState = EntityState.Added; + // this._commitState = CommitState.Unchanged; + // this._version = 1; + var properties = this.GetType().GetProperties(); + foreach (var property in properties) + { + this._originalValues.Add(property.Name, property.GetValue(this)); + } + + return this; + } + + public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, + IAccessContext accessContext, + IUUIdProvider idProvider) + { + this.Validate(); + + this._commitState = CommitState.Accepted; + var (now, accessUserId) = (systemClock.GetNow(), accessContext.GetUserId()); + + + if (this.EntityState == ChangeState.Added) + { + this._id = idProvider.GenerateId(); + this.CreatedAt = now; + this.CreatedBy = accessUserId; + this.Version = 1; + } + else + { + this.Version = this.Version++; + } + + this.ModifiedAt = now; + this.ModifiedBy = accessUserId; + + // this._entityState = EntityState.Submitted; + + return (null, true); + } + + public abstract void RejectChanges(); + + public Dictionary GetChangedProperties() + { + return this._changedProperties; + } + + public ChangeState EntityState + { + get => this._entityState; + init => this._entityState = value; + } + + public CommitState CommitState + { + get => this._commitState; + init => this._commitState = value; + } + + public int Version { get; set; } + + public Dictionary GetOriginalValues() + { + return this._originalValues; + } + + public Guid Id { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string? CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string? ModifiedBy { get; set; } + + public void ChangeTrack(string propertyName, object value) + { + this.Validate(); + + var changes = this._changedProperties; + var originals = this._originalValues; + if (originals.Count <= 0) + { + throw new Exception("尚未啟用追蹤"); + } + + if (changes.ContainsKey(propertyName) == false) + { + if (originals[propertyName] != value) + { + changes.Add(propertyName, value); + this._entityState = ChangeState.Modified; + } + } + else + { + if (originals[propertyName].ToString() == value.ToString()) + { + changes.Remove(propertyName); + } + } + + if (changes.Count <= 0) + { + } + } + + private void Validate() + { + if (this.CommitState == CommitState.Accepted) + { + throw new Exception("已經同意,無法再進行修改"); + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Error.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Error.cs new file mode 100644 index 00000000..aa8f1b7a --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Error.cs @@ -0,0 +1,3 @@ +namespace Lab.ChangeTracking.Domain; + +public record Error(T Code, object Message); \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeContent.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeContent.cs new file mode 100644 index 00000000..6d899ee9 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeContent.cs @@ -0,0 +1,8 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeContent +{ + Dictionary GetChangedProperties(); + + Dictionary GetOriginalValues(); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeState.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeState.cs new file mode 100644 index 00000000..d3223700 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeState.cs @@ -0,0 +1,10 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeState +{ + ChangeState EntityState { get; init; } + + CommitState CommitState { get; init; } + + int Version { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTime.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTime.cs new file mode 100644 index 00000000..c987e109 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTime.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace Lab.ChangeTracking.Domain; + +public interface IChangeTime +{ + public Guid Id { get; set; } + + DateTimeOffset CreatedAt { get; set; } + + string CreatedBy { get; set; } + + DateTimeOffset? ModifiedAt { get; set; } + + string? ModifiedBy { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs new file mode 100644 index 00000000..9708f0ea --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IChangeTrackable.cs @@ -0,0 +1,12 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IChangeTrackable : IChangeTime, IChangeState +{ + public (Error err, bool changed) AcceptChanges(ISystemClock systemClock, + IAccessContext accessContext, + IUUIdProvider idProvider); + + void ChangeTrack(string propertyName, object value); + + void RejectChanges(); +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ISystemClock.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ISystemClock.cs new file mode 100644 index 00000000..50076e0d --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/ISystemClock.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface ISystemClock +{ + DateTimeOffset GetNow(); +} + +public class SystemClock : ISystemClock +{ + public DateTimeOffset GetNow() + { + return DateTimeOffset.Now; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs new file mode 100644 index 00000000..a58ce071 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/IUUIdProvider.cs @@ -0,0 +1,14 @@ +namespace Lab.ChangeTracking.Domain; + +public interface IUUIdProvider +{ + Guid GenerateId(); +} + +public class UUIdProvider : IUUIdProvider +{ + public Guid GenerateId() + { + return Guid.NewGuid(); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj new file mode 100644 index 00000000..4249eea0 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/Lab.ChangeTracking.Domain.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/TypeConverterExtension.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/TypeConverterExtension.cs new file mode 100644 index 00000000..04617675 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Domain/TypeConverterExtension.cs @@ -0,0 +1,114 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +namespace Lab.ChangeTracking.Domain; + +public static class TypeConverterExtension +{ + public static EmployeeEntity To(this Employee source) + { + return new EmployeeEntity + { + Id = source.Id, + Version = source.Version, + Name = source.Name, + Age = source.Age, + Addresses = source.Addresses.To().ToList(), + Identity = source.Identity?.To(), + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy, + Remark = source.Remark + }; + } + + public static Employee To(this EmployeeEntity source) + { + return new Employee + { + Id = source.Id, + Version = source.Version, + Name = source.Name, + Age = source.Age, + Addresses = source.Addresses.To().ToList(), + Identity = source.Identity?.To(), + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy, + Remark = source.Remark + }; + } + + public static IdentityEntity To(this Identity source) + { + return new IdentityEntity + { + Employee_Id = source.Employee_Id, + Account = source.Account, + Password = source.Password, + Remark = source.Remark, + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy + }; + } + + public static Identity To(this IdentityEntity source) + { + return new Identity + { + Employee_Id = source.Employee_Id, + Account = source.Account, + Password = source.Password, + Remark = source.Remark, + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy + }; + } + + public static Address To(this AddressEntity source) + { + return new Address + { + Id = source.Id, + Employee_Id = source.Employee_Id, + Country = source.Country, + Street = source.Street, + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy, + Remark = source.Remark, + }; + } + + public static AddressEntity To(this Address source) + { + return new AddressEntity + { + Id = source.Id, + Employee_Id = source.Employee_Id, + Country = source.Country, + Street = source.Street, + CreatedAt = source.CreatedAt, + CreatedBy = source.CreatedBy, + ModifiedAt = source.ModifiedAt, + ModifiedBy = source.ModifiedBy, + Remark = source.Remark, + }; + } + + public static IEnumerable To(this IEnumerable
sources) + { + return sources.Select(p => p.To()); + } + + public static IEnumerable
To(this IEnumerable sources) + { + return sources.Select(p => p.To()); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..332cf598 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppDependencyInjectionExtensions.cs @@ -0,0 +1,48 @@ +using Lab.ChangeTracking.Infrastructure.DB.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ChangeTracking.Infrastructure.DB; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => { builder.AddConsole(); }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddPooledDbContextFactory((provider, optionsBuilder) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + var loggerFactory = provider.GetService(); + optionsBuilder.UseSqlServer(connectionString) + .UseLoggerFactory(loggerFactory) + .LogTo(Console.WriteLine) + .EnableSensitiveDataLogging() + ; + }); + + // services.AddPooledDbContextFactory((provider, options) => + // { + // var option = provider.GetService(); + // var loggerFactory = provider.GetService(); + // options.UseNpgsql( + // option.MemberDbConnectionString, //只會呼叫一次 + // builder => + // builder.EnableRetryOnFailure( + // 10, + // TimeSpan.FromSeconds(30), + // new List { "57P01" })) + // + // // .UseLazyLoadingProxies() + // .UseSnakeCaseNamingConvention() + // .UseLoggerFactory(loggerFactory) + // ; + // }); + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs new file mode 100644 index 00000000..d93a85cc --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/AppEnvironmentOption.cs @@ -0,0 +1,32 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + private string _employeeDbConnectionString; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs new file mode 100644 index 00000000..cdddbfa0 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Address.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel; + +public class Address +{ + public Guid Id { get; set; } + + public Guid Employee_Id { get; set; } + + public Employee Employee { get; set; } + + public string Country { get; set; } + + public string Street { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int SequenceId { get; set; } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs new file mode 100644 index 00000000..15e8c62b --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Employee.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Employee + { + public Guid Id { get; set; } + + public int Version { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + public IList
Addresses { get; set; } + + public Identity Identity { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..9ecfbc28 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,120 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Identity { get; set; } + + public virtual DbSet
Addresses { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + // 改用 CI 執行 Migrate + + // if (s_migrated[0]) + // { + // return; + // } + // + // lock (s_migrated) + // { + // if (s_migrated[0] == false) + // { + // var sqlOptions = options.FindExtension(); + // if (sqlOptions != null) + // { + // this.Database.Migrate(); + // } + // + // s_migrated[0] = true; + // } + // } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); + modelBuilder.ApplyConfiguration(new IdentityConfiguration()); + modelBuilder.ApplyConfiguration(new AddressConfiguration()); + } + + internal class EmployeeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Employee"); + builder.HasKey(p => p.Id) + .IsClustered(false); + + builder.Property(p => p.Name).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + + private class IdentityConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Identity"); + builder.HasKey(p => p.Employee_Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithOne(p => p.Identity) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade) + ; + + builder.Property(p => p.Account).IsRequired(); + builder.Property(p => p.Password).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + + private class AddressConfiguration : IEntityTypeConfiguration
+ { + public void Configure(EntityTypeBuilder
builder) + { + builder.ToTable("Address"); + builder.HasKey(p => p.Id).IsClustered(false); + builder.HasOne(e => e.Employee) + .WithMany(p => p.Addresses) + .HasForeignKey(p => p.Employee_Id) + .OnDelete(DeleteBehavior.Cascade); + + builder.Property(p => p.Country).IsRequired(); + builder.Property(p => p.Street).IsRequired(); + builder.Property(p => p.CreatedBy).IsRequired(); + builder.Property(p => p.CreatedAt).IsRequired(); + builder.Property(p => p.ModifiedBy).IsRequired(false); + builder.Property(p => p.ModifiedAt).IsRequired(false); + builder.Property(p => p.Remark).IsRequired(false); + + builder.HasIndex(e => e.SequenceId) + .IsUnique() + .IsClustered(); + } + } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs new file mode 100644 index 00000000..e9e2f6d9 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EntityModel/Identity.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ChangeTracking.Infrastructure.DB.EntityModel +{ + public class Identity + { + public Guid Employee_Id { get; set; } + + // [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string? ModifiedBy { get; set; } + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs new file mode 100644 index 00000000..b115d8a2 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ChangeTracking.Infrastructure.DB; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj new file mode 100644 index 00000000..3a36e2d1 --- /dev/null +++ b/Property Change Tracking/ChangeTrackVersion/src/Lab.ChangeTracking.Infrastructure.DB/Lab.ChangeTracking.Infrastructure.DB.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + + + + + + + + + + From 394e993978e1b3f526cd1add40b88718a94587fb Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 27 Mar 2022 18:57:42 +0800 Subject: [PATCH 189/267] feat: add DictionaryStringObjectJsonConverter project --- Json/Lab.JsonConverter/Lab.JsonConverter.sln | 22 ++ ...ictionaryStringObjectJsonConverterTests.cs | 199 ++++++++++++++++++ .../Lab.JsonConverterLib.UnitTest.csproj | 21 ++ .../DictionaryStringObjectJsonConverter.cs | 114 ++++++++++ .../JsonDocumentExtensions.cs | 41 ++++ .../Lab.JsonConverterLib.csproj | 9 + 6 files changed, 406 insertions(+) create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverter.sln create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter.cs create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonDocumentExtensions.cs create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib/Lab.JsonConverterLib.csproj diff --git a/Json/Lab.JsonConverter/Lab.JsonConverter.sln b/Json/Lab.JsonConverter/Lab.JsonConverter.sln new file mode 100644 index 00000000..07e7f922 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverter.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.JsonConverterLib", "Lab.JsonConverterLib\Lab.JsonConverterLib.csproj", "{5E5A6E58-2175-435A-AC74-617DBF7A70F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.JsonConverterLib.UnitTest", "Lab.JsonConverterLib.UnitTest\Lab.JsonConverterLib.UnitTest.csproj", "{E36B4A45-46F6-4709-ACF6-2887F4D26B5D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5E5A6E58-2175-435A-AC74-617DBF7A70F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E5A6E58-2175-435A-AC74-617DBF7A70F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E5A6E58-2175-435A-AC74-617DBF7A70F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E5A6E58-2175-435A-AC74-617DBF7A70F2}.Release|Any CPU.Build.0 = Release|Any CPU + {E36B4A45-46F6-4709-ACF6-2887F4D26B5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E36B4A45-46F6-4709-ACF6-2887F4D26B5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E36B4A45-46F6-4709-ACF6-2887F4D26B5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E36B4A45-46F6-4709-ACF6-2887F4D26B5D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs new file mode 100644 index 00000000..60e751a9 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.JsonConverterLib.UnitTest; + +[TestClass] +public class DictionaryStringObjectJsonConverterTests +{ + [TestMethod] + public void JsonDocument轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + var json = JsonSerializer.Serialize(expected); + + using var jsonDoc = json.ToJsonDocument(); + var actual = jsonDoc.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void JsonDocument轉Dictionary1() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + using var jsonDoc = expected.ToJsonDocument(); + var actual = jsonDoc.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void Memory轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + + var expected = new Dictionary + { + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["anonymousType"] = new { Prop = 123 }, + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var json = JsonSerializer.Serialize(expected, options); + var jsonMemory = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + var actual = JsonSerializer.Deserialize>(jsonMemory, options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void 字串轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var json = JsonSerializer.Serialize(expected, options); + var actual = JsonSerializer.Deserialize>(json, options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void 字串轉Dictionary_失敗() + { + var expected = new Dictionary + { + ["i"] = 255, + ["s"] = "字串", + ["d"] = new DateTime(1900, 1, 1), + ["a"] = new[] { 1, 2 }, + ["o"] = new { Prop = 123 } + }; + var json = JsonSerializer.Serialize(expected); + + var actual = JsonSerializer.Deserialize>(json); + Assert.AreNotEqual(expected["i"], actual["i"]); + Assert.AreNotEqual(expected["s"], actual["s"]); + + // 反序列化之後得到 JsonElement Type,必須要要透過其他手段才能取得真實的值 + Assert.AreEqual("JsonElement", actual["s"].GetType().Name); + Assert.AreEqual(expected["i"], ((JsonElement)actual["i"]).GetInt32()); + Assert.AreEqual(expected["s"], ((JsonElement)actual["s"]).GetString()); + } + + private static void AssertAnonymousType(Dictionary actual) + { + var expected = new Dictionary + { + { "Prop", (long)123 } + }; + + Assert.AreEqual(expected["Prop"], actual["Prop"]); + } + + private static void AssertDecimalArray(List actual) + { + var expected = new List + { + (long)1, + (decimal)2.1 + }; + + Assert.AreEqual(expected[0], actual[0]); + Assert.AreEqual(expected[1], actual[1]); + } + + private class Model + { + public string Name { get; set; } + + public int Age { get; set; } + } +} \ No newline at end of file diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj new file mode 100644 index 00000000..d0b60f4b --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter.cs new file mode 100644 index 00000000..28ecf719 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter.cs @@ -0,0 +1,114 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lab.JsonConverterLib; + +public class DictionaryStringObjectJsonConverter : JsonConverter> +{ + public override Dictionary Read(ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); + } + + var results = new Dictionary(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return results; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("JsonTokenType was not PropertyName"); + } + + var propertyName = reader.GetString(); + + if (string.IsNullOrWhiteSpace(propertyName)) + { + throw new JsonException("Failed to get property name"); + } + + reader.Read(); + + results.Add(propertyName, this.ReadValue(ref reader, options)); + } + + return results; + } + + public override void Write(Utf8JsonWriter writer, + Dictionary value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach (var key in value.Keys) + { + WriteValue(writer, key, value[key], options); + } + + writer.WriteEndObject(); + } + + private object ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + if (reader.TryGetDateTimeOffset(out var dateOffset)) + { + return dateOffset; + } + + if (reader.TryGetGuid(out var guid)) + { + return guid; + } + + return reader.GetString(); + case JsonTokenType.False: + case JsonTokenType.True: + return reader.GetBoolean(); + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + if (reader.TryGetInt64(out var result)) + { + return result; + } + + return reader.GetDecimal(); + case JsonTokenType.StartObject: + return this.Read(ref reader, null, options); + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + list.Add(this.ReadValue(ref reader, options)); + } + + return list; + default: + throw new JsonException($"'{reader.TokenType}' is not supported"); + } + } + + private static void WriteValue(Utf8JsonWriter writer, + string key, + object value, + JsonSerializerOptions options) + { + if (key != null) + { + writer.WritePropertyName(key); + } + + JsonSerializer.Serialize(writer, value, options); + } +} \ No newline at end of file diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonDocumentExtensions.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonDocumentExtensions.cs new file mode 100644 index 00000000..c97c67eb --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonDocumentExtensions.cs @@ -0,0 +1,41 @@ +using System.Text; +using System.Text.Json; + +namespace Lab.JsonConverterLib; + +public static class JsonDocumentExtensions +{ + public static T To(this JsonDocument source, + JsonSerializerOptions options = default) + { + return source.Deserialize(options); + } + + public static JsonDocument ToJsonDocument(this T source, + JsonDocumentOptions options = default) + where T : class + { + return JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(source), options); + } + + public static JsonDocument ToJsonDocument(this string source, + JsonDocumentOptions options = default) + { + return JsonDocument.Parse(source, options); + } + + public static string ToJsonString(this JsonDocument source, + JsonWriterOptions options = default) + { + if (source == null) + { + return null; + } + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, options); + source.WriteTo(writer); + writer.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); + } +} \ No newline at end of file diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib/Lab.JsonConverterLib.csproj b/Json/Lab.JsonConverter/Lab.JsonConverterLib/Lab.JsonConverterLib.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib/Lab.JsonConverterLib.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + From 8d6902fd20c24bbc676bdf18b9658beffe4287a3 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 27 Mar 2022 19:21:30 +0800 Subject: [PATCH 190/267] feat: add JsonNodeExtensions --- ...ictionaryStringObjectJsonConverterTests.cs | 51 ++++++++++++++++--- .../JsonNodeExtensions.cs | 42 +++++++++++++++ 2 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonNodeExtensions.cs diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs index 60e751a9..48c87558 100644 --- a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs @@ -20,13 +20,13 @@ public void JsonDocument轉Dictionary() var expected = new Dictionary { ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "yao" }, + ["model"] = new Model { Age = 19, Name = "小章" }, ["null"] = null!, ["dateTimeOffset"] = DateTimeOffset.Now, ["long"] = (long)255, ["decimal"] = (decimal)3.1416, ["guid"] = Guid.NewGuid(), - ["string"] = "String", + ["string"] = "字串", ["decimalArray"] = new[] { 1, (decimal)2.1 }, }; var json = JsonSerializer.Serialize(expected); @@ -54,13 +54,13 @@ public void JsonDocument轉Dictionary1() var expected = new Dictionary { ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "yao" }, + ["model"] = new Model { Age = 19, Name = "小章" }, ["null"] = null!, ["dateTimeOffset"] = DateTimeOffset.Now, ["long"] = (long)255, ["decimal"] = (decimal)3.1416, ["guid"] = Guid.NewGuid(), - ["string"] = "String", + ["string"] = "字串", ["decimalArray"] = new[] { 1, (decimal)2.1 }, }; @@ -77,6 +77,40 @@ public void JsonDocument轉Dictionary1() AssertDecimalArray(actual["decimalArray"] as List); } + [TestMethod] + public void JsonsNode轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + var json = JsonSerializer.Serialize(expected); + + var jsonObject = json.ToJsonNode(); + var actual = jsonObject.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + [TestMethod] public void Memory轉Dictionary() { @@ -87,13 +121,14 @@ public void Memory轉Dictionary() var expected = new Dictionary { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, ["null"] = null!, ["dateTimeOffset"] = DateTimeOffset.Now, ["long"] = (long)255, ["decimal"] = (decimal)3.1416, ["guid"] = Guid.NewGuid(), - ["string"] = "String", - ["anonymousType"] = new { Prop = 123 }, + ["string"] = "字串", ["decimalArray"] = new[] { 1, (decimal)2.1 }, }; @@ -122,13 +157,13 @@ public void 字串轉Dictionary() var expected = new Dictionary { ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "yao" }, + ["model"] = new Model { Age = 19, Name = "小章" }, ["null"] = null!, ["dateTimeOffset"] = DateTimeOffset.Now, ["long"] = (long)255, ["decimal"] = (decimal)3.1416, ["guid"] = Guid.NewGuid(), - ["string"] = "String", + ["string"] = "字串", ["decimalArray"] = new[] { 1, (decimal)2.1 }, }; diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonNodeExtensions.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonNodeExtensions.cs new file mode 100644 index 00000000..cb7593d4 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib/JsonNodeExtensions.cs @@ -0,0 +1,42 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Lab.JsonConverterLib; + +public static class JsonNodeExtensions +{ + public static T To(this JsonNode source, + JsonSerializerOptions options = default) + { + return source.Deserialize(options); + } + + public static JsonNode ToJsonNode(this T source, + JsonNodeOptions options = default) + where T : class + { + return JsonNode.Parse(JsonSerializer.SerializeToUtf8Bytes(source), options); + } + + public static JsonNode ToJsonNode(this string source, + JsonNodeOptions options = default) + { + return JsonNode.Parse(source, options); + } + + public static string ToJsonString(this JsonNode source, + JsonWriterOptions options = default) + { + if (source == null) + { + return null; + } + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, options); + source.WriteTo(writer); + writer.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); + } +} \ No newline at end of file From 4452fc49081b40cfb6d3625a094e6041e6bc8de4 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 28 Mar 2022 16:54:10 +0800 Subject: [PATCH 191/267] add project --- .../GlobalSteps.cs | 24 +++++ .../Lab.ORM.DynamicField.UnitTest.csproj | 30 ++++++ .../TestAssistant.cs | 39 ++++++++ .../UnitTest1.cs | 97 +++++++++++++++++++ .../Lab.ORM.DynamicField.sln | 27 ++++++ .../AppDependencyInjectionExtensions.cs | 51 ++++++++++ .../AppEnvironmentOption.cs | 32 ++++++ .../EntityModel/Employee.cs | 35 +++++++ .../EntityModel/EmployeeDbContext.cs | 41 ++++++++ .../EntityModel/Identity.cs | 33 +++++++ .../EntityModel/OrderHistory.cs | 30 ++++++ .../EnvironmentAssistant.cs | 15 +++ .../Lab.ORM.DynamicField.csproj | 18 ++++ .../Lab.ORM.DynamicField/Makefile | 4 + ORM/Lab.ORM.DynamicField/docker-compose.yml | 9 ++ 15 files changed, 485 insertions(+) create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/TestAssistant.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.sln create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppEnvironmentOption.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Identity.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/OrderHistory.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EnvironmentAssistant.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Makefile create mode 100644 ORM/Lab.ORM.DynamicField/docker-compose.yml diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs new file mode 100644 index 00000000..5dc33201 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs @@ -0,0 +1,24 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ORM.DynamicField.UnitTest; + +[TestClass] +public class GlobalSteps +{ + [AssemblyCleanup] + public static void Cleanup() + { + TestAssistant.SetTestEnvironmentVariable(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + db.Database.EnsureDeleted(); + } + + [AssemblyInitialize] + public static void Setup(TestContext context) + { + TestAssistant.SetTestEnvironmentVariable(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + db.Database.EnsureDeleted(); + db.Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj new file mode 100644 index 00000000..116efd17 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj @@ -0,0 +1,30 @@ + + + + net6.0 + enable + + false + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/TestAssistant.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/TestAssistant.cs new file mode 100644 index 00000000..be4cb416 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/TestAssistant.cs @@ -0,0 +1,39 @@ +using System; +using Lab.ORM.DynamicField.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.ORM.DynamicField.UnitTest; + +internal class TestAssistant +{ + private static IServiceProvider _serviceProvider; + + public static IDbContextFactory EmployeeDbContextFactory => + _serviceProvider.GetService>(); + + static TestAssistant() + { + var services = new ServiceCollection(); + ConfigureTestServices(services); + } + + public static void ConfigureTestServices(IServiceCollection services) + { + services.AddAppEnvironment(); + services.AddEntityFramework(); + _serviceProvider = services.BuildServiceProvider(); + } + + public static void SetTestEnvironmentVariable() + { + var connectionString = + "Host=localhost;Port=5432;Database=employee;Username=postgres;Password=guest;"; + + // var connectionString = + // "Data Source=localhost;Initial Catalog=EmployeeDb;Integrated Security=false;User ID=sa;Password=pass@w0rd1~;MultipleActiveResultSets=True;TrustServerCertificate=True"; + + var option = _serviceProvider.GetService(); + option.EmployeeDbConnectionString = connectionString; + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs new file mode 100644 index 00000000..3cbd4716 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using EFCore.BulkExtensions; +using Faker; +using Lab.ORM.DynamicField.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ORM.DynamicField.UnitTest; + +[TestClass] +public class UnitTest1 +{ + [TestCleanup] + public void TestCleanup() + { + CleanData(); + } + + [TestInitialize] + public void TestInitialize() + { + CleanData(); + } + + [TestMethod] + public void TestMethod1() + { + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + + // var id = Guid.NewGuid(); + // db.Employees.Add(new Employee + // { + // Id = id, + // Age = RandomNumber.Next(1, 100), + // Name = Name.FullName(), + // CreatedAt = DateTimeOffset.UtcNow, + // CreatedBy = "Sys", + // + // // Identity = new Identity + // // { + // // Account = "yao", + // // CreateAt = DateTimeOffset.UtcNow, + // // CreateBy = "Sys", + // // Password = "123456", + // // }, + // }); + var generateEmployees = GenerateEmployees(1); + db.AddRange(generateEmployees); + db.SaveChanges(); + } + + [TestMethod] + public void TestMethod2() + { + var host = CreateHostBuilder(null).Start(); + host.Services.GetService>(); + } + + private static void CleanData() + { + using var dbContext = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + dbContext.Employees.BatchDelete(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureServices((hostBuilder, services) => { TestAssistant.ConfigureTestServices(services); }); + } + + private static List GenerateEmployees(int totalCount) + { + var employees = Enumerable.Range(0, totalCount) + .Select((x, i) => + { + var now = DateTimeOffset.UtcNow; + var sysAccount = "sys"; + return new Employee + { + Id = Guid.NewGuid(), + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedBy = sysAccount, + CreatedAt = now, + ModifiedAt = null, + ModifiedBy = null, + + // Name = Name.First(), + }; + }).ToList(); + return employees; + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.sln b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.sln new file mode 100644 index 00000000..0ff4a8f9 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ORM.DynamicField", "Lab.ORM.DynamicField\Lab.ORM.DynamicField.csproj", "{C1348BD0-4FCA-4DF6-9C94-B2543F4B6E88}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.ORM.DynamicField.UnitTest", "Lab.ORM.DynamicField.UnitTest\Lab.ORM.DynamicField.UnitTest.csproj", "{BC21E9D0-E865-499B-8395-4067E5E7DB09}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{B1431E24-8BEB-4481-9E42-8B66BBF99494}" + ProjectSection(SolutionItems) = preProject + docker-compose.yml = docker-compose.yml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C1348BD0-4FCA-4DF6-9C94-B2543F4B6E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C1348BD0-4FCA-4DF6-9C94-B2543F4B6E88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1348BD0-4FCA-4DF6-9C94-B2543F4B6E88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C1348BD0-4FCA-4DF6-9C94-B2543F4B6E88}.Release|Any CPU.Build.0 = Release|Any CPU + {BC21E9D0-E865-499B-8395-4067E5E7DB09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC21E9D0-E865-499B-8395-4067E5E7DB09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC21E9D0-E865-499B-8395-4067E5E7DB09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC21E9D0-E865-499B-8395-4067E5E7DB09}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs new file mode 100644 index 00000000..1edc7fb4 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs @@ -0,0 +1,51 @@ +using Lab.ORM.DynamicField.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lab.ORM.DynamicField; + +public static class AppDependencyInjectionExtensions +{ + public static void AddAppEnvironment(this IServiceCollection services) + { + services.AddLogging(builder => { builder.AddConsole(); }); + services.AddSingleton(); + } + + public static void AddEntityFramework(this IServiceCollection services) + { + services.AddDbContextFactory((provider, options) => + { + var option = provider.GetService(); + var connectionString = option.EmployeeDbConnectionString; + options.UseNpgsql(connectionString, //只會呼叫一次 + builder => builder.EnableRetryOnFailure( + 10, + TimeSpan.FromSeconds(30), + new List { "57P01" }) + ) + + // .UseLazyLoadingProxies() + .EnableSensitiveDataLogging() //这将捕获通过迁移发送的更改。 + .LogTo(Console.WriteLine, LogLevel.Information) //这将捕获所有发送到数据库的SQL。 + // .UseLoggerFactory(loggerFactory) + ; + + //.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + }); + } + + // public static void AddEntityFramework(this IServiceCollection services) + // { + // services.AddPooledDbContextFactory((provider, optionsBuilder) => + // { + // var option = provider.GetService(); + // var connectionString = option.EmployeeDbConnectionString; + // var loggerFactory = provider.GetService(); + // optionsBuilder.UseNpgsql(connectionString) + // .UseLoggerFactory(loggerFactory) + // ; + // }); + // } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppEnvironmentOption.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppEnvironmentOption.cs new file mode 100644 index 00000000..93d18070 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppEnvironmentOption.cs @@ -0,0 +1,32 @@ +namespace Lab.ORM.DynamicField; + +public class AppEnvironmentOption +{ + public string EmployeeDbConnectionString + { + get + { + if (string.IsNullOrWhiteSpace(this._employeeDbConnectionString)) + { + this._employeeDbConnectionString = + EnvironmentAssistant.GetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR); + } + + return this._employeeDbConnectionString; + } + set + { + this._employeeDbConnectionString = value; + Environment.SetEnvironmentVariable(this.EMPLOYEE_DB_CONN_STR, value); + } + } + + private readonly string EMPLOYEE_DB_CONN_STR = "EMPLOYEE_DB_CONNECTION_STR"; + + private string _employeeDbConnectionString; + + public void Initial() + { + var memberDbConnectionString = this.EmployeeDbConnectionString; + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs new file mode 100644 index 00000000..33c3810f --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ORM.DynamicField.EntityModel +{ + [Table("Employee")] + public class Employee + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } + + public int? Age { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + // public virtual Identity Identity { get; set; } + + [Required] + public DateTimeOffset CreatedAt { get; set; } + + [Required] + public string CreatedBy { get; set; } + + public DateTimeOffset? ModifiedAt { get; set; } + + public string ModifiedBy { get; set; } + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs new file mode 100644 index 00000000..3910ea26 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; + +namespace Lab.ORM.DynamicField.EntityModel +{ + public class EmployeeDbContext : DbContext + { + private static readonly bool[] s_migrated = { false }; + + public virtual DbSet Employees { get; set; } + + // public virtual DbSet Identities { get; set; } + // + // public virtual DbSet OrderHistories { get; set; } + + public EmployeeDbContext(DbContextOptions options) + : base(options) + { + } + + //管理索引 + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(p => + { + p.Property(p => p.Name).IsRequired().HasMaxLength(50); + p.Property(p => p.CreatedAt).IsRequired(); + p.Property(p => p.CreatedBy).IsRequired(); + p.Property(p => p.ModifiedAt).IsRequired(false); + p.Property(p => p.ModifiedBy).IsRequired(false); + p.Property(p => p.Remark).IsRequired(false); + }); + + modelBuilder.Entity(p => + { + p.HasIndex(e => e.SequenceId) + .IsUnique() + ; + }); + } + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Identity.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Identity.cs new file mode 100644 index 00000000..a62f5b10 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Identity.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ORM.DynamicField.EntityModel +{ + [Table("Identity")] + public class Identity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Employee_Id { get; set; } + + [Required] + public string Account { get; set; } + + [Required] + public string Password { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Remark { get; set; } + + [Required] + public DateTimeOffset CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + + [ForeignKey("Employee_Id")] + public virtual Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/OrderHistory.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/OrderHistory.cs new file mode 100644 index 00000000..14415d94 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/OrderHistory.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Lab.ORM.DynamicField.EntityModel +{ + [Table("OrderHistory")] + public class OrderHistory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid? Employee_Id { get; set; } + + public string Remark { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long SequenceId { get; set; } + + public string Product_Id { get; set; } + + public string Product_Name { get; set; } + + [Required] + public DateTime CreateAt { get; set; } + + [Required] + public string CreateBy { get; set; } + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EnvironmentAssistant.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EnvironmentAssistant.cs new file mode 100644 index 00000000..14b3d04c --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EnvironmentAssistant.cs @@ -0,0 +1,15 @@ +namespace Lab.ORM.DynamicField; + +public class EnvironmentAssistant +{ + public static string GetEnvironmentVariable(string key) + { + var result = Environment.GetEnvironmentVariable(key); + if (string.IsNullOrWhiteSpace(result)) + { + throw new Exception($"the key '{key}' not exist in environment variable"); + } + + return result; + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj new file mode 100644 index 00000000..e7d06e72 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Makefile b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Makefile new file mode 100644 index 00000000..cf17f07f --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Makefile @@ -0,0 +1,4 @@ +G1: + echo 'Hello World' +G2: + cmd \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/docker-compose.yml b/ORM/Lab.ORM.DynamicField/docker-compose.yml new file mode 100644 index 00000000..0c353024 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.8" + +services: + db: + image: postgres:12-alpine + environment: + - POSTGRES_PASSWORD=guest + ports: + - 5432:5432 \ No newline at end of file From 9ff043bae14d3ba9c03ff7ee415624c8f4fef2ab Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 28 Mar 2022 23:05:17 +0800 Subject: [PATCH 192/267] feat: add DictionaryStringObjectJsonConverter --- .../GlobalSteps.cs | 6 +- .../UnitTest1.cs | 182 +++++++++++++++--- .../AppDependencyInjectionExtensions.cs | 2 +- .../Lab.ORM.DynamicField/Customer.cs | 28 +++ .../DictionaryStringObjectJsonConverter.cs | 114 +++++++++++ .../EntityModel/Employee.cs | 19 +- .../EntityModel/EmployeeDbContext.cs | 3 + .../JsonDocumentExtensions.cs | 41 ++++ .../Lab.ORM.DynamicField.csproj | 6 +- 9 files changed, 367 insertions(+), 34 deletions(-) create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Customer.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/DictionaryStringObjectJsonConverter.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/JsonDocumentExtensions.cs diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs index 5dc33201..589504b3 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs @@ -8,9 +8,9 @@ public class GlobalSteps [AssemblyCleanup] public static void Cleanup() { - TestAssistant.SetTestEnvironmentVariable(); - using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); - db.Database.EnsureDeleted(); + // TestAssistant.SetTestEnvironmentVariable(); + // using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + // db.Database.EnsureDeleted(); } [AssemblyInitialize] diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs index 3cbd4716..d13799ce 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using EFCore.BulkExtensions; using Faker; using Lab.ORM.DynamicField.EntityModel; @@ -17,7 +18,7 @@ public class UnitTest1 [TestCleanup] public void TestCleanup() { - CleanData(); + // CleanData(); } [TestInitialize] @@ -27,37 +28,111 @@ public void TestInitialize() } [TestMethod] - public void TestMethod1() + public void TestMethod2() + { + var host = CreateHostBuilder(null).Start(); + host.Services.GetService>(); + } + + [TestMethod] + public void 查詢所有資料() { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + .Where(p => p.Id == newEmployee.Id) + .Select(p => new + { + Profiles = p.Profiles.To>(options), + }) + .FirstOrDefault(); + } - // var id = Guid.NewGuid(); - // db.Employees.Add(new Employee - // { - // Id = id, - // Age = RandomNumber.Next(1, 100), - // Name = Name.FullName(), - // CreatedAt = DateTimeOffset.UtcNow, - // CreatedBy = "Sys", - // - // // Identity = new Identity - // // { - // // Account = "yao", - // // CreateAt = DateTimeOffset.UtcNow, - // // CreateBy = "Sys", - // // Password = "123456", - // // }, - // }); - var generateEmployees = GenerateEmployees(1); - db.AddRange(generateEmployees); - db.SaveChanges(); + [TestMethod] + public void 查詢特定欄位_JsonDoc() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + + // .Where(p => p.Profiles.RootElement.GetProperty("long").GetString() == "255") + .Where(p => p.Profiles.RootElement.GetProperty("long").GetInt64() == 255) + .Select(p => new + { + Profiles = p.Profiles.To>(options), + }) + .FirstOrDefault(); } [TestMethod] - public void TestMethod2() + public void 查詢特定欄位_POCO() { - var host = CreateHostBuilder(null).Start(); - host.Services.GetService>(); + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + .Where(p => p.Customer.Age > 12) + .Select(p => new + { + p.Customer + + // Order = p.Customer.Orders.Select(p => new { p.Price, p.ShippingAddress }) + // Order = p.Customer + // .Orders + // .Select(p => new Order + // { + // Price = p.Price + // }) + // + + // aa = p.Customer.Orders.ToDictionary(p => p.Price, p => p.ShippingAddress) + }) + + // .AsAsyncEnumerable() + .FirstOrDefault() + ; + } + + [TestMethod] + public void 新增資料() + { + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var id = Guid.NewGuid(); + db.Employees.Add(new Employee + { + Id = id, + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedAt = DateTimeOffset.UtcNow, + CreatedBy = "Sys", + Profiles = expected.ToJsonDocument(), + }); + + db.SaveChanges(); } private static void CleanData() @@ -94,4 +169,61 @@ private static List GenerateEmployees(int totalCount) }).ToList(); return employees; } + + private static Employee Insert() + { + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var id = Guid.NewGuid(); + var newEmployee = new Employee + { + Id = id, + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedAt = DateTimeOffset.UtcNow, + CreatedBy = "Sys", + Profiles = expected.ToJsonDocument(), + Customer = new Customer + { + Age = 19, + Name = "小章", + Orders = new[] + { + new Order + { + Price = (decimal)22.1, + ShippingAddress = "台北市" + } + }, + Product = new Product + { + Id = Guid.NewGuid(), + Name = "Mouse" + } + } + }; + db.Employees.Add(newEmployee); + + db.SaveChanges(); + return newEmployee; + } +} + +public record Model +{ + public int Age { get; set; } + + public string Name { get; set; } } \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs index 1edc7fb4..7b23b667 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/AppDependencyInjectionExtensions.cs @@ -27,7 +27,7 @@ public static void AddEntityFramework(this IServiceCollection services) ) // .UseLazyLoadingProxies() - .EnableSensitiveDataLogging() //这将捕获通过迁移发送的更改。 + // .EnableSensitiveDataLogging() //这将捕获通过迁移发送的更改。 .LogTo(Console.WriteLine, LogLevel.Information) //这将捕获所有发送到数据库的SQL。 // .UseLoggerFactory(loggerFactory) ; diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Customer.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Customer.cs new file mode 100644 index 00000000..a0b74e88 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Customer.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; + +namespace Lab.ORM.DynamicField; + +public record Customer +{ + public string Name { get; set; } + + public int Age { get; set; } + + public Order[] Orders { get; set; } + + public Product Product { get; set; } +} + +public record Order +{ + // [JsonPropertyName("OrderPrice")] + public decimal Price { get; set; } + + public string ShippingAddress { get; set; } +} + +public record Product +{ + public Guid Id { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/DictionaryStringObjectJsonConverter.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/DictionaryStringObjectJsonConverter.cs new file mode 100644 index 00000000..745438fb --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/DictionaryStringObjectJsonConverter.cs @@ -0,0 +1,114 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lab.ORM.DynamicField; + +public class DictionaryStringObjectJsonConverter : JsonConverter> +{ + public override Dictionary Read(ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); + } + + var results = new Dictionary(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return results; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("JsonTokenType was not PropertyName"); + } + + var propertyName = reader.GetString(); + + if (string.IsNullOrWhiteSpace(propertyName)) + { + throw new JsonException("Failed to get property name"); + } + + reader.Read(); + + results.Add(propertyName, this.ReadValue(ref reader, options)); + } + + return results; + } + + public override void Write(Utf8JsonWriter writer, + Dictionary value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach (var key in value.Keys) + { + WriteValue(writer, key, value[key], options); + } + + writer.WriteEndObject(); + } + + private object ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + if (reader.TryGetDateTimeOffset(out var dateOffset)) + { + return dateOffset; + } + + if (reader.TryGetGuid(out var guid)) + { + return guid; + } + + return reader.GetString(); + case JsonTokenType.False: + case JsonTokenType.True: + return reader.GetBoolean(); + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + if (reader.TryGetInt64(out var result)) + { + return result; + } + + return reader.GetDecimal(); + case JsonTokenType.StartObject: + return this.Read(ref reader, null, options); + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + list.Add(this.ReadValue(ref reader, options)); + } + + return list; + default: + throw new JsonException($"'{reader.TokenType}' is not supported"); + } + } + + private static void WriteValue(Utf8JsonWriter writer, + string key, + object value, + JsonSerializerOptions options) + { + if (key != null) + { + writer.WritePropertyName(key); + } + + JsonSerializer.Serialize(writer, value, options); + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs index 33c3810f..97b69cfa 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/Employee.cs @@ -1,10 +1,11 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; namespace Lab.ORM.DynamicField.EntityModel { [Table("Employee")] - public class Employee + public class Employee : IDisposable { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -20,7 +21,19 @@ public class Employee public string Remark { get; set; } - // public virtual Identity Identity { get; set; } + public JsonDocument Profiles { get; set; } + + // [NotMapped] + public Customer Customer + { + get; + set; + + // get => _customer == null ? null : JsonSerializer.Deserialize(_customer); + // set => _customer = JsonSerializer.Serialize(value); + } + + internal string _customer; [Required] public DateTimeOffset CreatedAt { get; set; } @@ -31,5 +44,7 @@ public class Employee public DateTimeOffset? ModifiedAt { get; set; } public string ModifiedBy { get; set; } + + public void Dispose() => this.Profiles?.Dispose(); } } \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs index 3910ea26..c4a011da 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs @@ -22,6 +22,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity(p => { + p.Property(p=>p.Profiles).HasColumnType("jsonb"); + p.Property(p=>p.Customer).IsRequired(false).HasColumnType("jsonb"); + // p.Property(p => p._customer).HasColumnName("Customer").HasColumnType("jsonb"); p.Property(p => p.Name).IsRequired().HasMaxLength(50); p.Property(p => p.CreatedAt).IsRequired(); p.Property(p => p.CreatedBy).IsRequired(); diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/JsonDocumentExtensions.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/JsonDocumentExtensions.cs new file mode 100644 index 00000000..7af0b958 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/JsonDocumentExtensions.cs @@ -0,0 +1,41 @@ +using System.Text; +using System.Text.Json; + +namespace Lab.ORM.DynamicField; + +public static class JsonDocumentExtensions +{ + public static T To(this JsonDocument source, + JsonSerializerOptions options = default) + { + return source.Deserialize(options); + } + + public static JsonDocument ToJsonDocument(this T source, + JsonDocumentOptions options = default) + where T : class + { + return JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(source), options); + } + + public static JsonDocument ToJsonDocument(this string source, + JsonDocumentOptions options = default) + { + return JsonDocument.Parse(source, options); + } + + public static string ToJsonString(this JsonDocument source, + JsonWriterOptions options = default) + { + if (source == null) + { + return null; + } + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, options); + source.WriteTo(writer); + writer.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj index e7d06e72..65c79f52 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/Lab.ORM.DynamicField.csproj @@ -6,13 +6,13 @@ enable - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 092d3569e05a5bcf2c3e14798088e9e7b14019a0 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 29 Mar 2022 16:11:37 +0800 Subject: [PATCH 193/267] refactor --- .../Lab.ORM.DynamicField.UnitTest/UnitTest1.cs | 2 +- .../EntityModel/EmployeeDbContext.cs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs index d13799ce..74477e56 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs @@ -82,7 +82,7 @@ public void 查詢特定欄位_POCO() var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); var actual = db.Employees - .Where(p => p.Customer.Age > 12) + // .Where(p => p.Customer.Age > 12) .Select(p => new { p.Customer diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs index c4a011da..c094a419 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField/EntityModel/EmployeeDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; namespace Lab.ORM.DynamicField.EntityModel { @@ -20,10 +21,17 @@ public EmployeeDbContext(DbContextOptions options) //管理索引 protected override void OnModelCreating(ModelBuilder modelBuilder) { + var options = new JsonSerializerOptions(); modelBuilder.Entity(p => { - p.Property(p=>p.Profiles).HasColumnType("jsonb"); - p.Property(p=>p.Customer).IsRequired(false).HasColumnType("jsonb"); + p.Property(p => p.Profiles).HasColumnType("jsonb"); + p.Property(p => p.Customer) + .IsRequired(false) + .HasColumnType("jsonb") + // .HasConversion(p => JsonSerializer.Serialize(p, options), + // p => JsonSerializer.Deserialize(p, options)) + ; + // p.Property(p => p._customer).HasColumnName("Customer").HasColumnType("jsonb"); p.Property(p => p.Name).IsRequired().HasMaxLength(50); p.Property(p => p.CreatedAt).IsRequired(); From 34de8436471112cee37a3e3c998478e23338a426 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 30 Mar 2022 00:16:16 +0800 Subject: [PATCH 194/267] add raw sql --- .../{UnitTest1.cs => EfCoreRawSqlUnitTest.cs} | 25 +- .../EfCoreUnitTest.cs | 241 ++++++++++++++++++ .../Lab.ORM.DynamicField.UnitTest.csproj | 12 +- .../Lab.ORM.DynamicField.UnitTest/Model.cs | 8 + 4 files changed, 264 insertions(+), 22 deletions(-) rename ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/{UnitTest1.cs => EfCoreRawSqlUnitTest.cs} (94%) create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs create mode 100644 ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Model.cs diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs similarity index 94% rename from ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs rename to ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs index 74477e56..3f3cb2bf 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/UnitTest1.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs @@ -13,12 +13,12 @@ namespace Lab.ORM.DynamicField.UnitTest; [TestClass] -public class UnitTest1 +public class EFCoreRawSqlUnitTest { [TestCleanup] public void TestCleanup() { - // CleanData(); + CleanData(); } [TestInitialize] @@ -43,13 +43,12 @@ public void 查詢所有資料() }; var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); - var actual = db.Employees - .Where(p => p.Id == newEmployee.Id) - .Select(p => new - { - Profiles = p.Profiles.To>(options), - }) - .FirstOrDefault(); + + var actual = db.Employees.FromSqlRaw(@" +SELECT * +FROM ""Employee"" AS e +LIMIT 1 +").ToList(); } [TestMethod] @@ -82,6 +81,7 @@ public void 查詢特定欄位_POCO() var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); var actual = db.Employees + // .Where(p => p.Customer.Age > 12) .Select(p => new { @@ -219,11 +219,4 @@ private static Employee Insert() db.SaveChanges(); return newEmployee; } -} - -public record Model -{ - public int Age { get; set; } - - public string Name { get; set; } } \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs new file mode 100644 index 00000000..ffa2d421 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using EFCore.BulkExtensions; +using Faker; +using Lab.ORM.DynamicField.EntityModel; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.ORM.DynamicField.UnitTest; + +[TestClass] +public class EfCoreUnitTest +{ + [TestCleanup] + public void TestCleanup() + { + CleanData(); + } + + [TestInitialize] + public void TestInitialize() + { + CleanData(); + } + + [TestMethod] + public void TestMethod2() + { + var host = CreateHostBuilder(null).Start(); + host.Services.GetService>(); + } + + [TestMethod] + public void 查詢所有資料() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + .Where(p => p.Id == newEmployee.Id) + .Select(p => new + { + Profiles = p.Profiles.To>(options), + }) + .FirstOrDefault(); + } + + [TestMethod] + public void 查詢所有資料_Raw_SQL() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var queryCommand = @" +SELECT e.""Profiles"" +FROM ""Employee"" AS e +LIMIT 1 +"; + + var actual = db.Employees.FromSqlRaw(queryCommand); + } + + [TestMethod] + public void 查詢特定欄位_JsonDoc() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + + // .Where(p => p.Profiles.RootElement.GetProperty("long").GetString() == "255") + .Where(p => p.Profiles.RootElement.GetProperty("long").GetInt64() == 255) + .Select(p => new + { + Profiles = p.Profiles.To>(options), + }) + .FirstOrDefault(); + } + + [TestMethod] + public void 查詢特定欄位_POCO() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var newEmployee = Insert(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var actual = db.Employees + + // .Where(p => p.Customer.Age > 12) + .Select(p => new + { + p.Customer + + // Order = p.Customer.Orders.Select(p => new { p.Price, p.ShippingAddress }) + // Order = p.Customer + // .Orders + // .Select(p => new Order + // { + // Price = p.Price + // }) + // + + // aa = p.Customer.Orders.ToDictionary(p => p.Price, p => p.ShippingAddress) + }) + + // .AsAsyncEnumerable() + .FirstOrDefault() + ; + } + + [TestMethod] + public void 新增資料() + { + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var id = Guid.NewGuid(); + db.Employees.Add(new Employee + { + Id = id, + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedAt = DateTimeOffset.UtcNow, + CreatedBy = "Sys", + Profiles = expected.ToJsonDocument(), + }); + + db.SaveChanges(); + } + + private static void CleanData() + { + using var dbContext = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + dbContext.Employees.BatchDelete(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureServices((hostBuilder, services) => { TestAssistant.ConfigureTestServices(services); }); + } + + private static List GenerateEmployees(int totalCount) + { + var employees = Enumerable.Range(0, totalCount) + .Select((x, i) => + { + var now = DateTimeOffset.UtcNow; + var sysAccount = "sys"; + return new Employee + { + Id = Guid.NewGuid(), + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedBy = sysAccount, + CreatedAt = now, + ModifiedAt = null, + ModifiedBy = null, + + // Name = Name.First(), + }; + }).ToList(); + return employees; + } + + private static Employee Insert() + { + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "yao" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "String", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var id = Guid.NewGuid(); + var newEmployee = new Employee + { + Id = id, + Age = RandomNumber.Next(1, 100), + Name = Name.FullName(), + CreatedAt = DateTimeOffset.UtcNow, + CreatedBy = "Sys", + Profiles = expected.ToJsonDocument(), + Customer = new Customer + { + Age = 19, + Name = "小章", + Orders = new[] + { + new Order + { + Price = (decimal)22.1, + ShippingAddress = "台北市" + } + }, + Product = new Product + { + Id = Guid.NewGuid(), + Name = "Mouse" + } + } + }; + db.Employees.Add(newEmployee); + + db.SaveChanges(); + return newEmployee; + } +} \ No newline at end of file diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj index 116efd17..9512e0fb 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Lab.ORM.DynamicField.UnitTest.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -24,7 +24,7 @@ - + diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Model.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Model.cs new file mode 100644 index 00000000..766e0dc7 --- /dev/null +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/Model.cs @@ -0,0 +1,8 @@ +namespace Lab.ORM.DynamicField.UnitTest; + +public record Model +{ + public int Age { get; set; } + + public string Name { get; set; } +} \ No newline at end of file From 9d6e437de5f18e61122202548ad104e41f1f5d46 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 30 Mar 2022 11:41:41 +0800 Subject: [PATCH 195/267] feat: raw sql query --- .../EfCoreRawSqlUnitTest.cs | 1 + .../EfCoreUnitTest.cs | 40 +++++++++++-------- .../GlobalSteps.cs | 6 +-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs index 3f3cb2bf..b4e89ee2 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreRawSqlUnitTest.cs @@ -33,6 +33,7 @@ public void TestMethod2() var host = CreateHostBuilder(null).Start(); host.Services.GetService>(); } + [TestMethod] public void 查詢所有資料() diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs index ffa2d421..7b0b89a3 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/EfCoreUnitTest.cs @@ -35,7 +35,7 @@ public void TestMethod2() } [TestMethod] - public void 查詢所有資料() + public void 更新部分欄位() { var options = new JsonSerializerOptions { @@ -43,17 +43,23 @@ public void 查詢所有資料() }; var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); - var actual = db.Employees - .Where(p => p.Id == newEmployee.Id) - .Select(p => new - { - Profiles = p.Profiles.To>(options), - }) - .FirstOrDefault(); + var destEmployee = new Employee + { + Id = newEmployee.Id, + Name = "yao", + ModifiedAt = DateTimeOffset.UtcNow, + ModifiedBy = "sys" + }; + var employeeEntry = db.Entry(destEmployee); + db.Attach(destEmployee); + employeeEntry.Property(p => p.Name).IsModified = true; + employeeEntry.Property(p => p.ModifiedAt).IsModified = true; + employeeEntry.Property(p => p.ModifiedBy).IsModified = true; + var count = db.SaveChanges(); } [TestMethod] - public void 查詢所有資料_Raw_SQL() + public void 查詢所有資料() { var options = new JsonSerializerOptions { @@ -61,15 +67,15 @@ public void 查詢所有資料_Raw_SQL() }; var newEmployee = Insert(); using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); - var queryCommand = @" -SELECT e.""Profiles"" -FROM ""Employee"" AS e -LIMIT 1 -"; - - var actual = db.Employees.FromSqlRaw(queryCommand); + var actual = db.Employees + .Where(p => p.Id == newEmployee.Id) + .Select(p => new + { + Profiles = p.Profiles.To>(options), + }) + .FirstOrDefault(); } - + [TestMethod] public void 查詢特定欄位_JsonDoc() { diff --git a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs index 589504b3..5dc33201 100644 --- a/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs +++ b/ORM/Lab.ORM.DynamicField/Lab.ORM.DynamicField.UnitTest/GlobalSteps.cs @@ -8,9 +8,9 @@ public class GlobalSteps [AssemblyCleanup] public static void Cleanup() { - // TestAssistant.SetTestEnvironmentVariable(); - // using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); - // db.Database.EnsureDeleted(); + TestAssistant.SetTestEnvironmentVariable(); + using var db = TestAssistant.EmployeeDbContextFactory.CreateDbContext(); + db.Database.EnsureDeleted(); } [AssemblyInitialize] From 10aeb3d41f894a620b83d452387191b1712f1573 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 9 Apr 2022 15:38:25 +0800 Subject: [PATCH 196/267] add project --- .../DictionaryStringObjectJsonConverter.cs | 116 ++++++++++++++++++ ...DictionaryFluentValidation.UnitTest.csproj | 21 ++++ .../ProfileValidatorTests.cs | 30 +++++ .../Assistants/ProfileAssistants.cs | 40 ++++++ .../Fields/AddressFieldNames.cs | 30 +++++ .../Fields/BirthdayFieldNames.cs | 26 ++++ .../Fields/GenderFieldNames.cs | 26 ++++ .../Fields/NameFieldNames.cs | 26 ++++ .../Fields/PersonalFieldNames.cs | 52 ++++++++ .../Fields/ProfileFieldNames.cs | 35 ++++++ .../Lab.DictionaryFluentValidation.csproj | 13 ++ .../Validators/EmailFieldValidator.cs | 11 ++ .../Validators/ProfileValidator.cs | 97 +++++++++++++++ .../Validators/RequireFieldValidator.cs | 11 ++ .../Lab.DictionaryValidation.sln | 22 ++++ 15 files changed, 556 insertions(+) create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/Lab.DictionaryFluentValidation.UnitTest.csproj create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Lab.DictionaryFluentValidation.csproj create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryValidation.sln diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs new file mode 100644 index 00000000..85f37e52 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lab.DictionaryFluentValidation.UnitTest; + +public class DictionaryStringObjectJsonConverter : JsonConverter> +{ + public override Dictionary Read(ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); + } + + var results = new Dictionary(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return results; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("JsonTokenType was not PropertyName"); + } + + var propertyName = reader.GetString(); + + if (string.IsNullOrWhiteSpace(propertyName)) + { + throw new JsonException("Failed to get property name"); + } + + reader.Read(); + + results.Add(propertyName, this.ReadValue(ref reader, options)); + } + + return results; + } + + public override void Write(Utf8JsonWriter writer, + Dictionary value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach (var key in value.Keys) + { + WriteValue(writer, key, value[key], options); + } + + writer.WriteEndObject(); + } + + private object ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + if (reader.TryGetDateTimeOffset(out var dateOffset)) + { + return dateOffset; + } + + if (reader.TryGetGuid(out var guid)) + { + return guid; + } + + return reader.GetString(); + case JsonTokenType.False: + case JsonTokenType.True: + return reader.GetBoolean(); + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + if (reader.TryGetInt64(out var result)) + { + return result; + } + + return reader.GetDecimal(); + case JsonTokenType.StartObject: + return this.Read(ref reader, null, options); + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + list.Add(this.ReadValue(ref reader, options)); + } + + return list; + default: + throw new JsonException($"'{reader.TokenType}' is not supported"); + } + } + + private static void WriteValue(Utf8JsonWriter writer, + string key, + object value, + JsonSerializerOptions options) + { + if (key != null) + { + writer.WritePropertyName(key); + } + + JsonSerializer.Serialize(writer, value, options); + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/Lab.DictionaryFluentValidation.UnitTest.csproj b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/Lab.DictionaryFluentValidation.UnitTest.csproj new file mode 100644 index 00000000..3487bd02 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/Lab.DictionaryFluentValidation.UnitTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs new file mode 100644 index 00000000..6e0dbfba --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using Lab.DictionaryFluentValidation.Fields; +using Lab.DictionaryFluentValidation.Validators; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.DictionaryFluentValidation.UnitTest; + +[TestClass] +public class ProfileValidatorTests +{ + [TestMethod] + public void TestMethod1() + { + var data = new Dictionary() + { + { "contactEmail", "yao" }, + { "Name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, + }; + var options = new JsonSerializerOptions() + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs new file mode 100644 index 00000000..e645a267 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs @@ -0,0 +1,40 @@ +using System.Reflection; + +namespace Lab.DictionaryFluentValidation.Assistants; + +public class ProfileAssistants +{ + public static IEnumerable GetFieldNames() + { + var type = typeof(T); + + var bindingFlags = BindingFlags.Public + | BindingFlags.Static + ; + var results = new List(); + var fieldInfosInfos = type.GetFields(bindingFlags); + foreach (var fieldInfo in fieldInfosInfos) + { + results.Add(fieldInfo.GetValue(null).ToString()); + } + + return results; + } + + public static Dictionary GetFields() + { + var type = typeof(T); + + var bindingFlags = BindingFlags.Public + | BindingFlags.Static + ; + var results = new Dictionary(); + var fieldInfosInfos = type.GetFields(bindingFlags); + foreach (var fieldInfo in fieldInfosInfos) + { + results.Add(fieldInfo.GetValue(null).ToString(), null); + } + + return results; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs new file mode 100644 index 00000000..f7b314c2 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs @@ -0,0 +1,30 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class AddressFieldNames +{ + public static readonly string Address1 = "address1"; + public static readonly string Address2 = "address2"; + public static readonly string District = "district"; + public static readonly string CityTown = "cityTown"; + public static readonly string Province = "province"; + public static readonly string PostalCode = "postalCode"; + public static readonly string Country = "country"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetFields() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs new file mode 100644 index 00000000..f5bc2df9 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs @@ -0,0 +1,26 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class BirthdayFieldNames +{ + public static readonly string Year = "year"; + public static readonly string Month = "month"; + public static readonly string Day = "day"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetFields() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs new file mode 100644 index 00000000..41139a4c --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs @@ -0,0 +1,26 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class GenderFieldNames +{ + public static string Male = "male"; + public static string Female = "female"; + public static string NotAvailable = "notAvailable"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetFields() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs new file mode 100644 index 00000000..3bdba673 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs @@ -0,0 +1,26 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class NameFieldNames +{ + public static readonly string FirstName = "firstName"; + public static readonly string LastName = "lastName"; + public static readonly string FullName = "fullName"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetFields() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs new file mode 100644 index 00000000..69cd8501 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs @@ -0,0 +1,52 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class PersonalFieldNames +{ + /// + /// 身分證字號 + /// + public static string IdentityCardId = "identityCardId"; + + /// + /// 教育程度 + /// + public static string Education = "education"; + + /// + /// 職業 + /// + public static string Profession = "profession"; + + /// + /// 婚姻狀態 + /// + public static string MaritalStatus = "maritalStatus"; + + /// + /// 家屬 + /// + public static string Dependents = "dependents"; + + /// + /// 年收入 + /// + public static string AnnualIncome = "annualIncome"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetFields() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs new file mode 100644 index 00000000..21bcb040 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs @@ -0,0 +1,35 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class ProfileFieldNames +{ + public const string Name = "name"; + public const string Gender = "gender"; + public const string Birthday = "birthday"; + public const string EComJoinDateTime = "eComJoinDateTime"; + public const string MigrateJoinDateTime = "migrateJoinDateTime"; + public const string ContactEmail = "contactEmail"; + public const string Local = "locale"; + public const string BrandMemberId = "brandMemberId"; + public const string BarcodeValue = "barcodeValue"; + public const string Address = "address"; + public const string Personal = "personal"; + public const string Custom = "custom"; + + private static readonly Lazy> s_fieldNames = + new(ProfileAssistants.GetFieldNames); + + private static Lazy> s_fields = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldNames() + { + return s_fieldNames.Value; + } + + public static Dictionary GetFields() + { + return s_fields.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Lab.DictionaryFluentValidation.csproj b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Lab.DictionaryFluentValidation.csproj new file mode 100644 index 00000000..f03eb636 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Lab.DictionaryFluentValidation.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs new file mode 100644 index 00000000..d329fcd5 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class EmailFieldValidator : AbstractValidator +{ + public EmailFieldValidator() + { + this.RuleFor(customer => customer).EmailAddress(); + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs new file mode 100644 index 00000000..63d98237 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -0,0 +1,97 @@ +using FluentValidation; +using FluentValidation.Results; +using Lab.DictionaryFluentValidation.Fields; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class ProfileValidator : AbstractValidator> +{ + private readonly Lazy _emailFieldValidatorLazy = new(new EmailFieldValidator()); + + public ProfileValidator() + { + this._emailFieldValidatorLazy = new(new EmailFieldValidator()); + } + + protected override bool PreValidate(ValidationContext> context, ValidationResult result) + { + if (ValidateSupportFields(context) == false) + { + return false; + } + + var instances = context.InstanceToValidate; + foreach (var item in instances) + { + switch (item.Key) + { + case ProfileFieldNames.ContactEmail: + { + var validationResult = this._emailFieldValidatorLazy.Value.Validate(item.Value.ToString()); + if (validationResult.IsValid == false) + { + foreach (var error in validationResult.Errors) + { + var failure = new ValidationFailure(ProfileFieldNames.ContactEmail, + error.ErrorMessage, + error.AttemptedValue) + { + ErrorCode = error.ErrorCode, + }; + context.AddFailure(failure); + } + } + + break; + } + } + } + + return true; + } + + private static bool ValidateSupportFields(ValidationContext> context) + { + var instances = context.InstanceToValidate; + var isValid = true; + foreach (var item in instances) + { + var fieldName = item.Key; + var fieldValue = item.Value; + switch (fieldName) + { + case ProfileFieldNames.Name: + { + var propertyInfos = fieldValue.GetType().GetProperties(); + foreach (var propertyInfo in propertyInfos) + { + isValid = IsSupportFields(context, NameFieldNames.GetFields(), propertyInfo.Name); + } + + break; + } + default: + { + isValid = IsSupportFields(context, ProfileFieldNames.GetFields(), fieldName); + break; + } + } + } + + return isValid; + } + + private static bool IsSupportFields(ValidationContext> context, + Dictionary sourceFields, + string destFieldName) + { + var notExistKey = sourceFields.ContainsKey(destFieldName) == false; + if (notExistKey) + { + context.AddFailure(destFieldName, $"not support column '{destFieldName}'"); + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs new file mode 100644 index 00000000..6f81132b --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class RequireFieldValidator : AbstractValidator +{ + public RequireFieldValidator() + { + this.RuleFor(customer => customer).NotEmpty(); + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryValidation.sln b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryValidation.sln new file mode 100644 index 00000000..ab18b294 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryValidation.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DictionaryFluentValidation", "Lab.DictionaryFluentValidation\Lab.DictionaryFluentValidation.csproj", "{F351E4A0-8F8E-4CEF-BE2A-D158E420DFB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.DictionaryFluentValidation.UnitTest", "Lab.DictionaryFluentValidation.UnitTest\Lab.DictionaryFluentValidation.UnitTest.csproj", "{3C5718DF-EA0D-472B-8067-B5C736923125}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F351E4A0-8F8E-4CEF-BE2A-D158E420DFB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F351E4A0-8F8E-4CEF-BE2A-D158E420DFB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F351E4A0-8F8E-4CEF-BE2A-D158E420DFB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F351E4A0-8F8E-4CEF-BE2A-D158E420DFB6}.Release|Any CPU.Build.0 = Release|Any CPU + {3C5718DF-EA0D-472B-8067-B5C736923125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C5718DF-EA0D-472B-8067-B5C736923125}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C5718DF-EA0D-472B-8067-B5C736923125}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C5718DF-EA0D-472B-8067-B5C736923125}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 5280afc4985e5283b9194be38e2676a86dee3033 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 00:12:02 +0800 Subject: [PATCH 197/267] add RequireFieldValidator --- .../ProfileValidatorTests.cs | 72 +++++++- .../Fields/NameFieldNames.cs | 6 +- .../Validators/EmailFieldValidator.cs | 2 +- .../Validators/ProfileValidator.cs | 166 ++++++++++++------ .../Validators/RequireFieldValidator.cs | 4 +- 5 files changed, 189 insertions(+), 61 deletions(-) diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index 6e0dbfba..dae5514d 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using Lab.DictionaryFluentValidation.Fields; using Lab.DictionaryFluentValidation.Validators; @@ -11,20 +12,81 @@ namespace Lab.DictionaryFluentValidation.UnitTest; public class ProfileValidatorTests { [TestMethod] - public void TestMethod1() + public void 郵件格式錯誤() { var data = new Dictionary() { { "contactEmail", "yao" }, - { "Name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, }; - var options = new JsonSerializerOptions() + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("contactEmail", actualError.PropertyName); + Assert.AreEqual("EmailValidator", actualError.ErrorCode); + Assert.AreEqual("'' is not a valid email address.", actualError.ErrorMessage); + } + + [TestMethod] + public void 必填欄位為空() + { + var data = new Dictionary() + { + { "name", new { firstName = "yao", lastName = "yu", fullName = "" } }, + }; + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("name.fullName", actualError.PropertyName); + Assert.AreEqual("NotEmptyValidator", actualError.ErrorCode); + Assert.AreEqual("'' must not be empty.", actualError.ErrorMessage); + } + + [TestMethod] + public void 使用不支援的Key() + { + var data = new Dictionary() + { + { "Hi", null }, + }; + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("Hi", actualError.PropertyName); + Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); + Assert.AreEqual("not support column 'Hi'", actualError.ErrorMessage); + } + + [TestMethod] + public void Key區分大小寫() + { + var data = new Dictionary() + { + { "Name", null }, + }; + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("Name", actualError.PropertyName); + Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); + Assert.AreEqual("not support column 'Name'", actualError.ErrorMessage); + } + + [TestMethod] + public void 使用支援的Key() + { + var data = new Dictionary() { - Converters = { new DictionaryStringObjectJsonConverter() } + { "name", null }, }; - var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); + Assert.AreEqual(true, validationResult.IsValid); } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs index 3bdba673..cd6c8dd1 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs @@ -4,9 +4,9 @@ namespace Lab.DictionaryFluentValidation.Fields; public class NameFieldNames { - public static readonly string FirstName = "firstName"; - public static readonly string LastName = "lastName"; - public static readonly string FullName = "fullName"; + public const string FirstName = "firstName"; + public const string LastName = "lastName"; + public const string FullName = "fullName"; private static readonly Lazy> s_fieldNameLazy = new(ProfileAssistants.GetFieldNames); diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs index d329fcd5..d9b46bc4 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs @@ -6,6 +6,6 @@ public class EmailFieldValidator : AbstractValidator { public EmailFieldValidator() { - this.RuleFor(customer => customer).EmailAddress(); + this.RuleFor(p => p).EmailAddress(); } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index 63d98237..c477c552 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -6,92 +6,158 @@ namespace Lab.DictionaryFluentValidation.Validators; public class ProfileValidator : AbstractValidator> { - private readonly Lazy _emailFieldValidatorLazy = new(new EmailFieldValidator()); + private static readonly Lazy s_emailFieldValidatorLazy = new(() => new EmailFieldValidator()); - public ProfileValidator() + private static readonly Lazy s_requireFieldValidatorLazy = + new(() => new RequireFieldValidator()); + + private static bool IsNotSupportFields(ValidationContext> context) { - this._emailFieldValidatorLazy = new(new EmailFieldValidator()); + var instances = context.InstanceToValidate; + var isNotSupports = new List(); + foreach (var item in instances) + { + var fieldName = item.Key; + var fieldValue = item.Value; + + switch (fieldName) + { + case ProfileFieldNames.Name: + isNotSupports.Add(IsNotSupportNestFields(NameFieldNames.GetFields(), fieldValue, context)); + break; + case ProfileFieldNames.Birthday: + isNotSupports.Add(IsNotSupportNestFields(BirthdayFieldNames.GetFields(), fieldValue, context)); + break; + default: + isNotSupports.Add(IsNotSupportFields(ProfileFieldNames.GetFields(), fieldName, context)); + break; + } + } + + return isNotSupports.Any(p => p); } - protected override bool PreValidate(ValidationContext> context, ValidationResult result) + private static bool IsNotSupportFields(Dictionary sourceFields, + string destFieldName, + ValidationContext> context) { - if (ValidateSupportFields(context) == false) + var isSNotSupport = sourceFields.ContainsKey(destFieldName) == false; + if (isSNotSupport) { - return false; + var failure = new ValidationFailure(destFieldName, + $"not support column '{destFieldName}'") + { + ErrorCode = "NotSupportValidator", + }; + context.AddFailure(failure); } - var instances = context.InstanceToValidate; - foreach (var item in instances) + return isSNotSupport; + } + + private static bool IsNotSupportNestFields(Dictionary sourceFields, + object destValue, + ValidationContext> context) + { + if (destValue == null) { - switch (item.Key) - { - case ProfileFieldNames.ContactEmail: - { - var validationResult = this._emailFieldValidatorLazy.Value.Validate(item.Value.ToString()); - if (validationResult.IsValid == false) - { - foreach (var error in validationResult.Errors) - { - var failure = new ValidationFailure(ProfileFieldNames.ContactEmail, - error.ErrorMessage, - error.AttemptedValue) - { - ErrorCode = error.ErrorCode, - }; - context.AddFailure(failure); - } - } + return false; + } - break; - } - } + var isNotSupports = new List(); + + var propertyInfos = destValue.GetType().GetProperties(); + foreach (var propertyInfo in propertyInfos) + { + isNotSupports.Add(IsNotSupportFields(sourceFields, propertyInfo.Name, context)); } - return true; + return isNotSupports.Any(p => p); } - private static bool ValidateSupportFields(ValidationContext> context) + protected override bool PreValidate(ValidationContext> context, ValidationResult result) { + if (IsNotSupportFields(context)) + { + return false; + } + var instances = context.InstanceToValidate; - var isValid = true; foreach (var item in instances) { var fieldName = item.Key; var fieldValue = item.Value; + if (fieldValue == null) + { + continue; + } + switch (fieldName) { - case ProfileFieldNames.Name: + case ProfileFieldNames.ContactEmail: { - var propertyInfos = fieldValue.GetType().GetProperties(); - foreach (var propertyInfo in propertyInfos) - { - isValid = IsSupportFields(context, NameFieldNames.GetFields(), propertyInfo.Name); - } - + ValidateEmail(fieldValue.ToString(), context); break; } - default: + case ProfileFieldNames.Name: { - isValid = IsSupportFields(context, ProfileFieldNames.GetFields(), fieldName); + this.ValidateName(fieldName, fieldValue, context); break; } } } - return isValid; + return true; + } + + private static void ValidateEmail(string value, ValidationContext> context) + { + var validationResult = s_emailFieldValidatorLazy.Value.Validate(value); + ValidationContextAddFailure(ProfileFieldNames.ContactEmail, validationResult, context); + } + + private void ValidateName(string fieldName, + object fieldValue, ValidationContext> context) + { + var propertyInfos = fieldValue.GetType().GetProperties(); + foreach (var propertyInfo in propertyInfos) + { + var value = propertyInfo.GetValue(fieldValue); + switch (propertyInfo.Name) + { + case NameFieldNames.FullName: + ValidateRequireField(context, $"{fieldName}.{NameFieldNames.FullName}", value); + break; + } + } + } + + private static void ValidateRequireField(ValidationContext> context, + string fieldName, + object fieldValue) + { + var validationResult = s_requireFieldValidatorLazy.Value.Validate(fieldValue); + ValidationContextAddFailure(fieldName, validationResult, context); } - private static bool IsSupportFields(ValidationContext> context, - Dictionary sourceFields, - string destFieldName) + private static void ValidationContextAddFailure(string fieldName, + ValidationResult validationResult, + ValidationContext> context) { - var notExistKey = sourceFields.ContainsKey(destFieldName) == false; - if (notExistKey) + if (validationResult.IsValid) { - context.AddFailure(destFieldName, $"not support column '{destFieldName}'"); - return false; + return; } - return true; + foreach (var error in validationResult.Errors) + { + var failure = new ValidationFailure(fieldName, + error.ErrorMessage, + error.AttemptedValue) + { + ErrorCode = error.ErrorCode, + }; + context.AddFailure(failure); + } } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs index 6f81132b..58013712 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs @@ -2,10 +2,10 @@ namespace Lab.DictionaryFluentValidation.Validators; -public class RequireFieldValidator : AbstractValidator +public class RequireFieldValidator : AbstractValidator { public RequireFieldValidator() { - this.RuleFor(customer => customer).NotEmpty(); + this.RuleFor(p => p).NotEmpty().NotNull(); } } \ No newline at end of file From c869ca9b96c4c1ff486f87ca4072702610fdffae Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 11:54:37 +0800 Subject: [PATCH 198/267] feat: NameFieldValidator --- .../ProfileValidatorTests.cs | 8 +-- .../Validators/EmailFieldValidator.cs | 27 +++++++- .../Validators/NameFieldValidator.cs | 54 +++++++++++++++ .../Validators/ProfileValidator.cs | 69 +++---------------- 4 files changed, 93 insertions(+), 65 deletions(-) create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index dae5514d..e577a3f6 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -24,7 +24,7 @@ public void 郵件格式錯誤() var actualError = validationResult.Errors.First(); Assert.AreEqual("contactEmail", actualError.PropertyName); Assert.AreEqual("EmailValidator", actualError.ErrorCode); - Assert.AreEqual("'' is not a valid email address.", actualError.ErrorMessage); + Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); } [TestMethod] @@ -32,15 +32,15 @@ public void 必填欄位為空() { var data = new Dictionary() { - { "name", new { firstName = "yao", lastName = "yu", fullName = "" } }, + { "name", new { firstName = "yao", lastName = "", fullName = "" } }, }; var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); - Assert.AreEqual(false, validationResult.IsValid); + // Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("name.fullName", actualError.PropertyName); Assert.AreEqual("NotEmptyValidator", actualError.ErrorCode); - Assert.AreEqual("'' must not be empty.", actualError.ErrorMessage); + Assert.AreEqual("'name.fullName' must not be empty.", actualError.ErrorMessage); } [TestMethod] diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs index d9b46bc4..33c02738 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs @@ -1,11 +1,32 @@ using FluentValidation; +using FluentValidation.Results; +using Lab.DictionaryFluentValidation.Fields; namespace Lab.DictionaryFluentValidation.Validators; -public class EmailFieldValidator : AbstractValidator +public class EmailFieldValidator : AbstractValidator { - public EmailFieldValidator() + /// + /// return true 繼續往下驗證 + /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate + /// + /// + /// + /// + protected override bool PreValidate(ValidationContext context, ValidationResult result) { - this.RuleFor(p => p).EmailAddress(); + var isValid = true; + var instance = context.InstanceToValidate; + if (instance == null) + { + return isValid; + } + + this.RuleFor(p => p.ToString()) + .NotEmpty() + .WithName(ProfileFieldNames.ContactEmail) + .EmailAddress(); + ; + return isValid; } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs new file mode 100644 index 00000000..bda13d1e --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs @@ -0,0 +1,54 @@ +using FluentValidation; +using FluentValidation.Results; +using Lab.DictionaryFluentValidation.Fields; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class NameFieldValidator : AbstractValidator +{ + /// + /// return true 繼續往下驗證 + /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate + /// + /// + /// + /// + protected override bool PreValidate(ValidationContext context, ValidationResult result) + { + var isValid = true; + var instance = context.InstanceToValidate; + if (instance == null) + { + return isValid; + } + + var propertyInfos = instance.GetType().GetProperties(); + foreach (var propertyInfo in propertyInfos) + { + var value = propertyInfo.GetValue(instance); + if (value == null) + { + return isValid; + } + + var propertyName = $"name.{propertyInfo.Name}"; + switch (propertyInfo.Name) + { + case NameFieldNames.FirstName: + break; + case NameFieldNames.LastName: + break; + case NameFieldNames.FullName: + this.RuleFor(p => value) + .NotEmpty() + .WithName(propertyName) + .OverridePropertyName(propertyName) + ; + + break; + } + } + + return isValid; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index c477c552..6f37f469 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -6,11 +6,10 @@ namespace Lab.DictionaryFluentValidation.Validators; public class ProfileValidator : AbstractValidator> { - private static readonly Lazy s_emailFieldValidatorLazy = new(() => new EmailFieldValidator()); - - private static readonly Lazy s_requireFieldValidatorLazy = - new(() => new RequireFieldValidator()); - + private static readonly Lazy s_emailFieldValidatorLazy + = new(() => new EmailFieldValidator()); + private static readonly Lazy s_nameFieldValidatorLazy + = new(() => new NameFieldValidator()); private static bool IsNotSupportFields(ValidationContext> context) { var instances = context.InstanceToValidate; @@ -96,12 +95,17 @@ protected override bool PreValidate(ValidationContext { case ProfileFieldNames.ContactEmail: { - ValidateEmail(fieldValue.ToString(), context); + RuleFor(p => p[fieldName]) + .SetValidator(p => s_emailFieldValidatorLazy.Value) + ; + break; } case ProfileFieldNames.Name: { - this.ValidateName(fieldName, fieldValue, context); + RuleFor(p => p[fieldName]) + .SetValidator(p => s_nameFieldValidatorLazy.Value) + ; break; } } @@ -109,55 +113,4 @@ protected override bool PreValidate(ValidationContext return true; } - - private static void ValidateEmail(string value, ValidationContext> context) - { - var validationResult = s_emailFieldValidatorLazy.Value.Validate(value); - ValidationContextAddFailure(ProfileFieldNames.ContactEmail, validationResult, context); - } - - private void ValidateName(string fieldName, - object fieldValue, ValidationContext> context) - { - var propertyInfos = fieldValue.GetType().GetProperties(); - foreach (var propertyInfo in propertyInfos) - { - var value = propertyInfo.GetValue(fieldValue); - switch (propertyInfo.Name) - { - case NameFieldNames.FullName: - ValidateRequireField(context, $"{fieldName}.{NameFieldNames.FullName}", value); - break; - } - } - } - - private static void ValidateRequireField(ValidationContext> context, - string fieldName, - object fieldValue) - { - var validationResult = s_requireFieldValidatorLazy.Value.Validate(fieldValue); - ValidationContextAddFailure(fieldName, validationResult, context); - } - - private static void ValidationContextAddFailure(string fieldName, - ValidationResult validationResult, - ValidationContext> context) - { - if (validationResult.IsValid) - { - return; - } - - foreach (var error in validationResult.Errors) - { - var failure = new ValidationFailure(fieldName, - error.ErrorMessage, - error.AttemptedValue) - { - ErrorCode = error.ErrorCode, - }; - context.AddFailure(failure); - } - } } \ No newline at end of file From 507b4074beaee1be65b855a7ce658cf411667367 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 12:50:26 +0800 Subject: [PATCH 199/267] feat: BirthdayFieldValidator --- .../ProfileValidatorTests.cs | 85 +++++++++++++---- .../Fields/BirthdayFieldNames.cs | 6 +- .../Validators/BirthdayFieldValidator.cs | 91 +++++++++++++++++++ .../Validators/NameFieldValidator.cs | 2 +- .../Validators/ProfileValidator.cs | 16 +++- .../Validators/RequireFieldValidator.cs | 11 --- 6 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs delete mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index e577a3f6..f3a0831a 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -1,8 +1,5 @@ -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text.Json; -using Lab.DictionaryFluentValidation.Fields; using Lab.DictionaryFluentValidation.Validators; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,30 +9,32 @@ namespace Lab.DictionaryFluentValidation.UnitTest; public class ProfileValidatorTests { [TestMethod] - public void 郵件格式錯誤() + public void Key區分大小寫() { - var data = new Dictionary() + var data = new Dictionary { - { "contactEmail", "yao" }, + { "Name", null }, }; + var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("contactEmail", actualError.PropertyName); - Assert.AreEqual("EmailValidator", actualError.ErrorCode); - Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); + Assert.AreEqual("Name", actualError.PropertyName); + Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); + Assert.AreEqual("not support column 'Name'", actualError.ErrorMessage); } [TestMethod] public void 必填欄位為空() { - var data = new Dictionary() + var data = new Dictionary { { "name", new { firstName = "yao", lastName = "", fullName = "" } }, }; var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); + // Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("name.fullName", actualError.PropertyName); @@ -46,7 +45,7 @@ public void 必填欄位為空() [TestMethod] public void 使用不支援的Key() { - var data = new Dictionary() + var data = new Dictionary { { "Hi", null }, }; @@ -61,32 +60,78 @@ public void 使用不支援的Key() } [TestMethod] - public void Key區分大小寫() + public void 使用支援的Key() { - var data = new Dictionary() + var data = new Dictionary { - { "Name", null }, + { "name", null }, + }; + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(true, validationResult.IsValid); + } + + [TestMethod] + public void 二月三十是非法日期() + { + var data = new Dictionary + { + { "birthday", new { year = 2000, month = 2, day = 30 } }, }; var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("Name", actualError.PropertyName); - Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); - Assert.AreEqual("not support column 'Name'", actualError.ErrorMessage); + Assert.AreEqual("birthday", actualError.PropertyName); + Assert.AreEqual("BirthdayFieldValidator", actualError.ErrorCode); + Assert.AreEqual("year:2000,month:2,day:31 is invalid date format", actualError.ErrorMessage); } [TestMethod] - public void 使用支援的Key() + public void 沒有年是非法日期() { - var data = new Dictionary() + var data = new Dictionary { - { "name", null }, + { "birthday", new { month = 2, day = 30 } }, + }; + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("birthday.year", actualError.PropertyName); + Assert.AreEqual("BirthdayFieldValidator", actualError.ErrorCode); + Assert.AreEqual("'birthday.year' must not be empty.", actualError.ErrorMessage); + } + + [TestMethod] + public void 日期內容為非法值() + { + var data = new Dictionary + { + { "birthday", null }, }; var profileValidator = new ProfileValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(true, validationResult.IsValid); } + + [TestMethod] + public void 郵件格式錯誤() + { + var data = new Dictionary + { + { "contactEmail", "yao" }, + }; + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("contactEmail", actualError.PropertyName); + Assert.AreEqual("EmailValidator", actualError.ErrorCode); + Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); + } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs index f5bc2df9..4f0471af 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs @@ -4,9 +4,9 @@ namespace Lab.DictionaryFluentValidation.Fields; public class BirthdayFieldNames { - public static readonly string Year = "year"; - public static readonly string Month = "month"; - public static readonly string Day = "day"; + public const string Year = "year"; + public const string Month = "month"; + public const string Day = "day"; private static readonly Lazy> s_fieldNameLazy = new(ProfileAssistants.GetFieldNames); diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs new file mode 100644 index 00000000..c544d5b6 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs @@ -0,0 +1,91 @@ +using FluentValidation; +using FluentValidation.Results; +using Lab.DictionaryFluentValidation.Fields; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class BirthdayFieldValidator : AbstractValidator +{ + private const string ErrorCode = nameof(BirthdayFieldValidator); + + /// + /// return true 繼續往下驗證 + /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate + /// + /// + /// + /// + protected override bool PreValidate(ValidationContext context, ValidationResult result) + { + var isValid = true; + var instance = context.InstanceToValidate; + if (instance == null) + { + return isValid; + } + + var propertyInfos = instance.GetType().GetProperties(); + var birthday = new Dictionary(); + foreach (var propertyInfo in propertyInfos) + { + var value = propertyInfo.GetValue(instance); + if (value == null) + { + continue; + } + + birthday.Add(propertyInfo.Name, Convert.ToInt32(value)); + } + + var srcBirthdayFields = BirthdayFieldNames.GetFields(); + isValid = HasRequireField(context, srcBirthdayFields, birthday); + if (isValid == false) + { + return isValid; + } + + var year = birthday[BirthdayFieldNames.Year]; + var month = birthday[BirthdayFieldNames.Month]; + var day = birthday[BirthdayFieldNames.Day]; + try + { + var birthday2 = new DateTime(year, month, day); + } + catch (Exception e) + { + var errorMsg = $"{BirthdayFieldNames.Year}:{year}," + + $"{BirthdayFieldNames.Month}:{month}," + + $"{BirthdayFieldNames.Day}:{day} is invalid date format"; + + var validationFailure = new ValidationFailure(ProfileFieldNames.Birthday, errorMsg) + { + ErrorCode = ErrorCode + }; + context.AddFailure(validationFailure); + } + + return isValid; + } + + private static bool HasRequireField(ValidationContext context, Dictionary srcBirthdayFields, + Dictionary destBirthdayFields) + { + var isValid = true; + foreach (var srcField in srcBirthdayFields) + { + var srcKey = srcField.Key; + if (destBirthdayFields.ContainsKey(srcKey) == false) + { + var propertyName = $"birthday.{srcKey}"; + var validationFailure = new ValidationFailure(propertyName, $"'{propertyName}' must not be empty.") + { + ErrorCode = ErrorCode + }; + context.AddFailure(validationFailure); + isValid = false; + } + } + + return isValid; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs index bda13d1e..b99a212e 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs @@ -28,7 +28,7 @@ protected override bool PreValidate(ValidationContext context, Validatio var value = propertyInfo.GetValue(instance); if (value == null) { - return isValid; + continue; } var propertyName = $"name.{propertyInfo.Name}"; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index 6f37f469..07e261fb 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -6,10 +6,15 @@ namespace Lab.DictionaryFluentValidation.Validators; public class ProfileValidator : AbstractValidator> { - private static readonly Lazy s_emailFieldValidatorLazy + private static readonly Lazy s_emailFieldValidatorLazy = new(() => new EmailFieldValidator()); - private static readonly Lazy s_nameFieldValidatorLazy + + private static readonly Lazy s_nameFieldValidatorLazy = new(() => new NameFieldValidator()); + + private static readonly Lazy s_birthdayFieldValidatorLazy + = new(() => new BirthdayFieldValidator()); + private static bool IsNotSupportFields(ValidationContext> context) { var instances = context.InstanceToValidate; @@ -108,6 +113,13 @@ protected override bool PreValidate(ValidationContext ; break; } + case ProfileFieldNames.Birthday: + { + RuleFor(p => p[fieldName]) + .SetValidator(p => s_birthdayFieldValidatorLazy.Value) + ; + break; + } } } diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs deleted file mode 100644 index 58013712..00000000 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/RequireFieldValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FluentValidation; - -namespace Lab.DictionaryFluentValidation.Validators; - -public class RequireFieldValidator : AbstractValidator -{ - public RequireFieldValidator() - { - this.RuleFor(p => p).NotEmpty().NotNull(); - } -} \ No newline at end of file From 9187478a9ecf3a05676c00603c239b77115afb48 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 13:15:47 +0800 Subject: [PATCH 200/267] feat GenderFieldValidator --- .../ProfileValidatorTests.cs | 16 ++++++++ .../Fields/GenderFieldNames.cs | 26 ------------ .../Fields/GenderFieldValues.cs | 26 ++++++++++++ .../Fields/ProfileFieldNames.cs | 7 ---- .../Validators/GenderFieldValidator.cs | 41 +++++++++++++++++++ .../Validators/ProfileValidator.cs | 10 +++++ 6 files changed, 93 insertions(+), 33 deletions(-) delete mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs create mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index f3a0831a..cb00e25e 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -134,4 +134,20 @@ public void 郵件格式錯誤() Assert.AreEqual("EmailValidator", actualError.ErrorCode); Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); } + [TestMethod] + public void 性別格式錯誤() + { + var data = new Dictionary + { + { "gender", "公的" }, + }; + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("gender", actualError.PropertyName); + Assert.AreEqual("GenderFieldValidator", actualError.ErrorCode); + Assert.AreEqual("'公的' is invalid value.", actualError.ErrorMessage); + } + } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs deleted file mode 100644 index 41139a4c..00000000 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldNames.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Lab.DictionaryFluentValidation.Assistants; - -namespace Lab.DictionaryFluentValidation.Fields; - -public class GenderFieldNames -{ - public static string Male = "male"; - public static string Female = "female"; - public static string NotAvailable = "notAvailable"; - - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); - - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); - - public static IEnumerable GetFieldNames() - { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetFields() - { - return s_fieldLazy.Value; - } -} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs new file mode 100644 index 00000000..75be1ca6 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs @@ -0,0 +1,26 @@ +using Lab.DictionaryFluentValidation.Assistants; + +namespace Lab.DictionaryFluentValidation.Fields; + +public class GenderFieldValues +{ + public const string Male = "male"; + public const string Female = "female"; + public const string NotAvailable = "notAvailable"; + + private static readonly Lazy> s_fieldNameLazy = + new(ProfileAssistants.GetFieldNames); + + private static readonly Lazy> s_fieldLazy = + new(ProfileAssistants.GetFields); + + public static IEnumerable GetFieldValues() + { + return s_fieldNameLazy.Value; + } + + public static Dictionary GetValues() + { + return s_fieldLazy.Value; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs index 21bcb040..581581d2 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs @@ -7,15 +7,8 @@ public class ProfileFieldNames public const string Name = "name"; public const string Gender = "gender"; public const string Birthday = "birthday"; - public const string EComJoinDateTime = "eComJoinDateTime"; - public const string MigrateJoinDateTime = "migrateJoinDateTime"; public const string ContactEmail = "contactEmail"; - public const string Local = "locale"; - public const string BrandMemberId = "brandMemberId"; - public const string BarcodeValue = "barcodeValue"; public const string Address = "address"; - public const string Personal = "personal"; - public const string Custom = "custom"; private static readonly Lazy> s_fieldNames = new(ProfileAssistants.GetFieldNames); diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs new file mode 100644 index 00000000..a43dc732 --- /dev/null +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs @@ -0,0 +1,41 @@ +using FluentValidation; +using FluentValidation.Results; +using Lab.DictionaryFluentValidation.Fields; + +namespace Lab.DictionaryFluentValidation.Validators; + +public class GenderFieldValidator : AbstractValidator +{ + private const string ErrorCode = nameof(GenderFieldValidator); + + /// + /// return true 繼續往下驗證 + /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate + /// + /// + /// + /// + protected override bool PreValidate(ValidationContext context, ValidationResult result) + { + var isValid = true; + var instance = context.InstanceToValidate; + if (instance == null) + { + return isValid; + } + + var srcValues = GenderFieldValues.GetValues(); + var destValue = instance.ToString(); + if (srcValues.ContainsKey(destValue) == false) + { + var validationFailure = new ValidationFailure("gender", + $"'{destValue}' is invalid value.") + { + ErrorCode = ErrorCode + }; + context.AddFailure(validationFailure); + } + + return isValid; + } +} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index 07e261fb..131eaf2b 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -15,6 +15,9 @@ private static readonly Lazy s_nameFieldValidatorLazy private static readonly Lazy s_birthdayFieldValidatorLazy = new(() => new BirthdayFieldValidator()); + private static readonly Lazy s_genderFieldValidatorLazy + = new(() => new GenderFieldValidator()); + private static bool IsNotSupportFields(ValidationContext> context) { var instances = context.InstanceToValidate; @@ -120,6 +123,13 @@ protected override bool PreValidate(ValidationContext ; break; } + case ProfileFieldNames.Gender: + { + RuleFor(p => p[fieldName]) + .SetValidator(p => s_genderFieldValidatorLazy.Value) + ; + break; + } } } From 60093acdc9c19ad6a30bac864aada29a4b69eff6 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 13:41:21 +0800 Subject: [PATCH 201/267] refactor --- .../ProfileValidatorTests.cs | 2 +- .../Fields/AddressFieldNames.cs | 30 ------------------- .../Fields/ProfileFieldNames.cs | 1 - .../Validators/ProfileValidator.cs | 25 +++++++++------- 4 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index cb00e25e..69d96626 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -86,7 +86,7 @@ public void 二月三十是非法日期() var actualError = validationResult.Errors.First(); Assert.AreEqual("birthday", actualError.PropertyName); Assert.AreEqual("BirthdayFieldValidator", actualError.ErrorCode); - Assert.AreEqual("year:2000,month:2,day:31 is invalid date format", actualError.ErrorMessage); + Assert.AreEqual("year:2000,month:2,day:30 is invalid date format", actualError.ErrorMessage); } [TestMethod] diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs deleted file mode 100644 index f7b314c2..00000000 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/AddressFieldNames.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Lab.DictionaryFluentValidation.Assistants; - -namespace Lab.DictionaryFluentValidation.Fields; - -public class AddressFieldNames -{ - public static readonly string Address1 = "address1"; - public static readonly string Address2 = "address2"; - public static readonly string District = "district"; - public static readonly string CityTown = "cityTown"; - public static readonly string Province = "province"; - public static readonly string PostalCode = "postalCode"; - public static readonly string Country = "country"; - - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); - - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); - - public static IEnumerable GetFieldNames() - { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetFields() - { - return s_fieldLazy.Value; - } -} \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs index 581581d2..13878179 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs @@ -8,7 +8,6 @@ public class ProfileFieldNames public const string Gender = "gender"; public const string Birthday = "birthday"; public const string ContactEmail = "contactEmail"; - public const string Address = "address"; private static readonly Lazy> s_fieldNames = new(ProfileAssistants.GetFieldNames); diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index 131eaf2b..263bd1a9 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -1,3 +1,4 @@ +using System.Linq.Expressions; using FluentValidation; using FluentValidation.Results; using Lab.DictionaryFluentValidation.Fields; @@ -90,6 +91,13 @@ protected override bool PreValidate(ValidationContext } var instances = context.InstanceToValidate; + this.SetValidateRule(instances); + + return true; + } + + private void SetValidateRule(Dictionary instances) + { foreach (var item in instances) { var fieldName = item.Key; @@ -98,41 +106,38 @@ protected override bool PreValidate(ValidationContext { continue; } - + switch (fieldName) { case ProfileFieldNames.ContactEmail: { - RuleFor(p => p[fieldName]) - .SetValidator(p => s_emailFieldValidatorLazy.Value) - ; - + this.RuleFor(p => p[fieldName]) + .SetValidator(p => s_emailFieldValidatorLazy.Value); + break; } case ProfileFieldNames.Name: { - RuleFor(p => p[fieldName]) + this.RuleFor(p => p[fieldName]) .SetValidator(p => s_nameFieldValidatorLazy.Value) ; break; } case ProfileFieldNames.Birthday: { - RuleFor(p => p[fieldName]) + this.RuleFor(p => p[fieldName]) .SetValidator(p => s_birthdayFieldValidatorLazy.Value) ; break; } case ProfileFieldNames.Gender: { - RuleFor(p => p[fieldName]) + this.RuleFor(p => p[fieldName]) .SetValidator(p => s_genderFieldValidatorLazy.Value) ; break; } } } - - return true; } } \ No newline at end of file From 1716a51d17f040cdbfe737a8199df421130678e9 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 13:58:28 +0800 Subject: [PATCH 202/267] remove file --- .../DictionaryStringObjectJsonConverter.cs | 116 ------------------ 1 file changed, 116 deletions(-) delete mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs deleted file mode 100644 index 85f37e52..00000000 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/DictionaryStringObjectJsonConverter.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Lab.DictionaryFluentValidation.UnitTest; - -public class DictionaryStringObjectJsonConverter : JsonConverter> -{ - public override Dictionary Read(ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); - } - - var results = new Dictionary(); - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndObject) - { - return results; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("JsonTokenType was not PropertyName"); - } - - var propertyName = reader.GetString(); - - if (string.IsNullOrWhiteSpace(propertyName)) - { - throw new JsonException("Failed to get property name"); - } - - reader.Read(); - - results.Add(propertyName, this.ReadValue(ref reader, options)); - } - - return results; - } - - public override void Write(Utf8JsonWriter writer, - Dictionary value, - JsonSerializerOptions options) - { - writer.WriteStartObject(); - - foreach (var key in value.Keys) - { - WriteValue(writer, key, value[key], options); - } - - writer.WriteEndObject(); - } - - private object ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) - { - switch (reader.TokenType) - { - case JsonTokenType.String: - if (reader.TryGetDateTimeOffset(out var dateOffset)) - { - return dateOffset; - } - - if (reader.TryGetGuid(out var guid)) - { - return guid; - } - - return reader.GetString(); - case JsonTokenType.False: - case JsonTokenType.True: - return reader.GetBoolean(); - case JsonTokenType.Null: - return null; - case JsonTokenType.Number: - if (reader.TryGetInt64(out var result)) - { - return result; - } - - return reader.GetDecimal(); - case JsonTokenType.StartObject: - return this.Read(ref reader, null, options); - case JsonTokenType.StartArray: - var list = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - list.Add(this.ReadValue(ref reader, options)); - } - - return list; - default: - throw new JsonException($"'{reader.TokenType}' is not supported"); - } - } - - private static void WriteValue(Utf8JsonWriter writer, - string key, - object value, - JsonSerializerOptions options) - { - if (key != null) - { - writer.WritePropertyName(key); - } - - JsonSerializer.Serialize(writer, value, options); - } -} \ No newline at end of file From dfc5d6e7b93b9c6a305b1e1c498db1786203408b Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 14:11:05 +0800 Subject: [PATCH 203/267] remove file --- .../Fields/PersonalFieldNames.cs | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs deleted file mode 100644 index 69cd8501..00000000 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/PersonalFieldNames.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Lab.DictionaryFluentValidation.Assistants; - -namespace Lab.DictionaryFluentValidation.Fields; - -public class PersonalFieldNames -{ - /// - /// 身分證字號 - /// - public static string IdentityCardId = "identityCardId"; - - /// - /// 教育程度 - /// - public static string Education = "education"; - - /// - /// 職業 - /// - public static string Profession = "profession"; - - /// - /// 婚姻狀態 - /// - public static string MaritalStatus = "maritalStatus"; - - /// - /// 家屬 - /// - public static string Dependents = "dependents"; - - /// - /// 年收入 - /// - public static string AnnualIncome = "annualIncome"; - - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); - - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); - - public static IEnumerable GetFieldNames() - { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetFields() - { - return s_fieldLazy.Value; - } -} \ No newline at end of file From 875783ffc6ba8f3817232be63f88b6e69c19e4ca Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 10 Apr 2022 23:10:18 +0800 Subject: [PATCH 204/267] refactor --- .../ProfileValidatorTests.cs | 20 ++++++++++++++++++- .../Assistants/ProfileAssistants.cs | 19 +----------------- .../Fields/BirthdayFieldNames.cs | 16 +++++---------- .../Fields/GenderFieldValues.cs | 16 +++++---------- .../Fields/NameFieldNames.cs | 18 ++++++----------- .../Fields/ProfileFieldNames.cs | 16 +++++---------- .../Validators/BirthdayFieldValidator.cs | 2 +- .../Validators/GenderFieldValidator.cs | 2 +- .../Validators/ProfileValidator.cs | 6 +++--- 9 files changed, 46 insertions(+), 69 deletions(-) diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index 69d96626..68628dde 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Lab.DictionaryFluentValidation.Fields; using Lab.DictionaryFluentValidation.Validators; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,6 +9,23 @@ namespace Lab.DictionaryFluentValidation.UnitTest; [TestClass] public class ProfileValidatorTests { + [TestMethod] + public void 通過驗證() + { + BirthdayFieldNames.GetFieldNames(); + BirthdayFieldNames.GetFieldNames(); + var data = new Dictionary + { + { "name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, + { "birthday", new { year = 2000, month = 2, day = 28 } }, + { "contactEmail", "yao@aa.bb" }, + }; + + var profileValidator = new ProfileValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(true, validationResult.IsValid); + } + [TestMethod] public void Key區分大小寫() { @@ -134,6 +152,7 @@ public void 郵件格式錯誤() Assert.AreEqual("EmailValidator", actualError.ErrorCode); Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); } + [TestMethod] public void 性別格式錯誤() { @@ -149,5 +168,4 @@ public void 性別格式錯誤() Assert.AreEqual("GenderFieldValidator", actualError.ErrorCode); Assert.AreEqual("'公的' is invalid value.", actualError.ErrorMessage); } - } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs index e645a267..f2ce8fb0 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Assistants/ProfileAssistants.cs @@ -4,24 +4,7 @@ namespace Lab.DictionaryFluentValidation.Assistants; public class ProfileAssistants { - public static IEnumerable GetFieldNames() - { - var type = typeof(T); - - var bindingFlags = BindingFlags.Public - | BindingFlags.Static - ; - var results = new List(); - var fieldInfosInfos = type.GetFields(bindingFlags); - foreach (var fieldInfo in fieldInfosInfos) - { - results.Add(fieldInfo.GetValue(null).ToString()); - } - - return results; - } - - public static Dictionary GetFields() + public static Dictionary GetFieldNames() { var type = typeof(T); diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs index 4f0471af..8b44733d 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs @@ -8,19 +8,13 @@ public class BirthdayFieldNames public const string Month = "month"; public const string Day = "day"; - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); + private static readonly Lazy> s_fieldNamesLazy = + new(() => ProfileAssistants.GetFieldNames()); - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); + private static Dictionary FieldNames => s_fieldNamesLazy.Value; - public static IEnumerable GetFieldNames() + public static Dictionary GetFieldNames() { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetFields() - { - return s_fieldLazy.Value; + return FieldNames; } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs index 75be1ca6..fdeaea12 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs @@ -8,19 +8,13 @@ public class GenderFieldValues public const string Female = "female"; public const string NotAvailable = "notAvailable"; - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); + private static readonly Lazy> s_fieldValuesLazy = + new(() => ProfileAssistants.GetFieldNames()); - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); + private static Dictionary FieldValues => s_fieldValuesLazy.Value; - public static IEnumerable GetFieldValues() + public static Dictionary GetFieldValues() { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetValues() - { - return s_fieldLazy.Value; + return FieldValues; } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs index cd6c8dd1..e42c485f 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs @@ -7,20 +7,14 @@ public class NameFieldNames public const string FirstName = "firstName"; public const string LastName = "lastName"; public const string FullName = "fullName"; + + private static readonly Lazy> s_fieldNamesLazy = + new(() => ProfileAssistants.GetFieldNames()); - private static readonly Lazy> s_fieldNameLazy = - new(ProfileAssistants.GetFieldNames); + private static Dictionary FieldNames => s_fieldNamesLazy.Value; - private static readonly Lazy> s_fieldLazy = - new(ProfileAssistants.GetFields); - - public static IEnumerable GetFieldNames() - { - return s_fieldNameLazy.Value; - } - - public static Dictionary GetFields() + public static Dictionary GetFieldNames() { - return s_fieldLazy.Value; + return FieldNames; } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs index 13878179..9d49c233 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs @@ -9,19 +9,13 @@ public class ProfileFieldNames public const string Birthday = "birthday"; public const string ContactEmail = "contactEmail"; - private static readonly Lazy> s_fieldNames = - new(ProfileAssistants.GetFieldNames); + private static readonly Lazy> s_fieldNamesLazy = + new(() => ProfileAssistants.GetFieldNames()); - private static Lazy> s_fields = - new(ProfileAssistants.GetFields); + private static Dictionary FieldNames => s_fieldNamesLazy.Value; - public static IEnumerable GetFieldNames() + public static Dictionary GetFieldNames() { - return s_fieldNames.Value; - } - - public static Dictionary GetFields() - { - return s_fields.Value; + return FieldNames; } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs index c544d5b6..64e53747 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs @@ -37,7 +37,7 @@ protected override bool PreValidate(ValidationContext context, Validatio birthday.Add(propertyInfo.Name, Convert.ToInt32(value)); } - var srcBirthdayFields = BirthdayFieldNames.GetFields(); + var srcBirthdayFields = BirthdayFieldNames.GetFieldNames(); isValid = HasRequireField(context, srcBirthdayFields, birthday); if (isValid == false) { diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs index a43dc732..0cab502d 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs @@ -24,7 +24,7 @@ protected override bool PreValidate(ValidationContext context, Validatio return isValid; } - var srcValues = GenderFieldValues.GetValues(); + var srcValues = GenderFieldValues.GetFieldValues(); var destValue = instance.ToString(); if (srcValues.ContainsKey(destValue) == false) { diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs index 263bd1a9..492de546 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs @@ -31,13 +31,13 @@ private static bool IsNotSupportFields(ValidationContext Date: Tue, 12 Apr 2022 00:10:20 +0800 Subject: [PATCH 205/267] refactor --- .../ProfileValidatorTests.cs | 32 ++++----- ...hdayFieldNames.cs => BirthdayTypeNames.cs} | 4 +- ...nderFieldValues.cs => GenderTypeValues.cs} | 4 +- .../{NameFieldNames.cs => NameTypeNames.cs} | 4 +- ...ofileFieldNames.cs => ProfileTypeNames.cs} | 4 +- ...dValidator.cs => BirthdayTypeValidator.cs} | 70 ++++++++++--------- ...ieldValidator.cs => EmailTypeValidator.cs} | 20 ++++-- ...eldValidator.cs => GenderTypeValidator.cs} | 16 +++-- ...FieldValidator.cs => NameTypeValidator.cs} | 17 +++-- ...leValidator.cs => ProfileTypeValidator.cs} | 49 ++++++------- 10 files changed, 121 insertions(+), 99 deletions(-) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/{BirthdayFieldNames.cs => BirthdayTypeNames.cs} (82%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/{GenderFieldValues.cs => GenderTypeValues.cs} (83%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/{NameFieldNames.cs => NameTypeNames.cs} (84%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/{ProfileFieldNames.cs => ProfileTypeNames.cs} (84%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/{BirthdayFieldValidator.cs => BirthdayTypeValidator.cs} (66%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/{EmailFieldValidator.cs => EmailTypeValidator.cs} (55%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/{GenderFieldValidator.cs => GenderTypeValidator.cs} (65%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/{NameFieldValidator.cs => NameTypeValidator.cs} (76%) rename ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/{ProfileValidator.cs => ProfileTypeValidator.cs} (70%) diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index 68628dde..51eb5067 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -12,8 +12,8 @@ public class ProfileValidatorTests [TestMethod] public void 通過驗證() { - BirthdayFieldNames.GetFieldNames(); - BirthdayFieldNames.GetFieldNames(); + BirthdayTypeNames.GetFieldNames(); + BirthdayTypeNames.GetFieldNames(); var data = new Dictionary { { "name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, @@ -21,7 +21,7 @@ public void 通過驗證() { "contactEmail", "yao@aa.bb" }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(true, validationResult.IsValid); } @@ -34,7 +34,7 @@ public void Key區分大小寫() { "Name", null }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); @@ -50,7 +50,7 @@ public void 必填欄位為空() { { "name", new { firstName = "yao", lastName = "", fullName = "" } }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); // Assert.AreEqual(false, validationResult.IsValid); @@ -68,7 +68,7 @@ public void 使用不支援的Key() { "Hi", null }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); @@ -85,7 +85,7 @@ public void 使用支援的Key() { "name", null }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(true, validationResult.IsValid); } @@ -98,12 +98,12 @@ public void 二月三十是非法日期() { "birthday", new { year = 2000, month = 2, day = 30 } }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("birthday", actualError.PropertyName); - Assert.AreEqual("BirthdayFieldValidator", actualError.ErrorCode); + Assert.AreEqual(nameof(BirthdayTypeValidator), actualError.ErrorCode); Assert.AreEqual("year:2000,month:2,day:30 is invalid date format", actualError.ErrorMessage); } @@ -115,12 +115,12 @@ public void 沒有年是非法日期() { "birthday", new { month = 2, day = 30 } }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("birthday.year", actualError.PropertyName); - Assert.AreEqual("BirthdayFieldValidator", actualError.ErrorCode); + Assert.AreEqual(nameof(BirthdayTypeValidator), actualError.ErrorCode); Assert.AreEqual("'birthday.year' must not be empty.", actualError.ErrorMessage); } @@ -132,7 +132,7 @@ public void 日期內容為非法值() { "birthday", null }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(true, validationResult.IsValid); } @@ -144,12 +144,12 @@ public void 郵件格式錯誤() { { "contactEmail", "yao" }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("contactEmail", actualError.PropertyName); - Assert.AreEqual("EmailValidator", actualError.ErrorCode); + Assert.AreEqual(nameof(EmailTypeValidator), actualError.ErrorCode); Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); } @@ -160,12 +160,12 @@ public void 性別格式錯誤() { { "gender", "公的" }, }; - var profileValidator = new ProfileValidator(); + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); Assert.AreEqual("gender", actualError.PropertyName); - Assert.AreEqual("GenderFieldValidator", actualError.ErrorCode); + Assert.AreEqual(nameof(GenderTypeValidator), actualError.ErrorCode); Assert.AreEqual("'公的' is invalid value.", actualError.ErrorMessage); } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayTypeNames.cs similarity index 82% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayTypeNames.cs index 8b44733d..0a5a89ba 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/BirthdayTypeNames.cs @@ -2,14 +2,14 @@ namespace Lab.DictionaryFluentValidation.Fields; -public class BirthdayFieldNames +public class BirthdayTypeNames { public const string Year = "year"; public const string Month = "month"; public const string Day = "day"; private static readonly Lazy> s_fieldNamesLazy = - new(() => ProfileAssistants.GetFieldNames()); + new(() => ProfileAssistants.GetFieldNames()); private static Dictionary FieldNames => s_fieldNamesLazy.Value; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderTypeValues.cs similarity index 83% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderTypeValues.cs index fdeaea12..670504e1 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderFieldValues.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/GenderTypeValues.cs @@ -2,14 +2,14 @@ namespace Lab.DictionaryFluentValidation.Fields; -public class GenderFieldValues +public class GenderTypeValues { public const string Male = "male"; public const string Female = "female"; public const string NotAvailable = "notAvailable"; private static readonly Lazy> s_fieldValuesLazy = - new(() => ProfileAssistants.GetFieldNames()); + new(() => ProfileAssistants.GetFieldNames()); private static Dictionary FieldValues => s_fieldValuesLazy.Value; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameTypeNames.cs similarity index 84% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameTypeNames.cs index e42c485f..0034bbe6 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/NameTypeNames.cs @@ -2,14 +2,14 @@ namespace Lab.DictionaryFluentValidation.Fields; -public class NameFieldNames +public class NameTypeNames { public const string FirstName = "firstName"; public const string LastName = "lastName"; public const string FullName = "fullName"; private static readonly Lazy> s_fieldNamesLazy = - new(() => ProfileAssistants.GetFieldNames()); + new(() => ProfileAssistants.GetFieldNames()); private static Dictionary FieldNames => s_fieldNamesLazy.Value; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileTypeNames.cs similarity index 84% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileTypeNames.cs index 9d49c233..e00d1b53 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileFieldNames.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Fields/ProfileTypeNames.cs @@ -2,7 +2,7 @@ namespace Lab.DictionaryFluentValidation.Fields; -public class ProfileFieldNames +public class ProfileTypeNames { public const string Name = "name"; public const string Gender = "gender"; @@ -10,7 +10,7 @@ public class ProfileFieldNames public const string ContactEmail = "contactEmail"; private static readonly Lazy> s_fieldNamesLazy = - new(() => ProfileAssistants.GetFieldNames()); + new(() => ProfileAssistants.GetFieldNames()); private static Dictionary FieldNames => s_fieldNamesLazy.Value; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayTypeValidator.cs similarity index 66% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayTypeValidator.cs index 64e53747..e88c1704 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/BirthdayTypeValidator.cs @@ -4,9 +4,37 @@ namespace Lab.DictionaryFluentValidation.Validators; -public class BirthdayFieldValidator : AbstractValidator +public class BirthdayTypeValidator : AbstractValidator { - private const string ErrorCode = nameof(BirthdayFieldValidator); + private const string ErrorCode = nameof(BirthdayTypeValidator); + private readonly string _propertyName; + + public BirthdayTypeValidator(string propertyName) + { + this._propertyName = propertyName; + } + + private bool HasRequireField(ValidationContext context, Dictionary srcBirthdayFields, + Dictionary destBirthdayFields) + { + var isValid = true; + foreach (var srcField in srcBirthdayFields) + { + var srcKey = srcField.Key; + if (destBirthdayFields.ContainsKey(srcKey) == false) + { + var propertyName = $"{_propertyName}.{srcKey}"; + var validationFailure = new ValidationFailure(propertyName, $"'{propertyName}' must not be empty.") + { + ErrorCode = ErrorCode + }; + context.AddFailure(validationFailure); + isValid = false; + } + } + + return isValid; + } /// /// return true 繼續往下驗證 @@ -37,27 +65,27 @@ protected override bool PreValidate(ValidationContext context, Validatio birthday.Add(propertyInfo.Name, Convert.ToInt32(value)); } - var srcBirthdayFields = BirthdayFieldNames.GetFieldNames(); + var srcBirthdayFields = BirthdayTypeNames.GetFieldNames(); isValid = HasRequireField(context, srcBirthdayFields, birthday); if (isValid == false) { return isValid; } - var year = birthday[BirthdayFieldNames.Year]; - var month = birthday[BirthdayFieldNames.Month]; - var day = birthday[BirthdayFieldNames.Day]; + var year = birthday[BirthdayTypeNames.Year]; + var month = birthday[BirthdayTypeNames.Month]; + var day = birthday[BirthdayTypeNames.Day]; try { var birthday2 = new DateTime(year, month, day); } catch (Exception e) { - var errorMsg = $"{BirthdayFieldNames.Year}:{year}," + - $"{BirthdayFieldNames.Month}:{month}," + - $"{BirthdayFieldNames.Day}:{day} is invalid date format"; + var errorMsg = $"{BirthdayTypeNames.Year}:{year}," + + $"{BirthdayTypeNames.Month}:{month}," + + $"{BirthdayTypeNames.Day}:{day} is invalid date format"; - var validationFailure = new ValidationFailure(ProfileFieldNames.Birthday, errorMsg) + var validationFailure = new ValidationFailure(this._propertyName, errorMsg) { ErrorCode = ErrorCode }; @@ -66,26 +94,4 @@ protected override bool PreValidate(ValidationContext context, Validatio return isValid; } - - private static bool HasRequireField(ValidationContext context, Dictionary srcBirthdayFields, - Dictionary destBirthdayFields) - { - var isValid = true; - foreach (var srcField in srcBirthdayFields) - { - var srcKey = srcField.Key; - if (destBirthdayFields.ContainsKey(srcKey) == false) - { - var propertyName = $"birthday.{srcKey}"; - var validationFailure = new ValidationFailure(propertyName, $"'{propertyName}' must not be empty.") - { - ErrorCode = ErrorCode - }; - context.AddFailure(validationFailure); - isValid = false; - } - } - - return isValid; - } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailTypeValidator.cs similarity index 55% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailTypeValidator.cs index 33c02738..9d3bcae3 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/EmailTypeValidator.cs @@ -1,14 +1,20 @@ using FluentValidation; using FluentValidation.Results; -using Lab.DictionaryFluentValidation.Fields; namespace Lab.DictionaryFluentValidation.Validators; -public class EmailFieldValidator : AbstractValidator +public class EmailTypeValidator : AbstractValidator { + private readonly string _propertyName; + + public EmailTypeValidator(string propertyName) + { + this._propertyName = propertyName; + } + /// - /// return true 繼續往下驗證 - /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate + /// return true 繼續往下驗證 + /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate /// /// /// @@ -23,9 +29,9 @@ protected override bool PreValidate(ValidationContext context, Validatio } this.RuleFor(p => p.ToString()) - .NotEmpty() - .WithName(ProfileFieldNames.ContactEmail) - .EmailAddress(); + .EmailAddress() + .WithName(this._propertyName) + .WithErrorCode(nameof(EmailTypeValidator)) ; return isValid; } diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderTypeValidator.cs similarity index 65% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderTypeValidator.cs index 0cab502d..998dbfab 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/GenderTypeValidator.cs @@ -4,9 +4,15 @@ namespace Lab.DictionaryFluentValidation.Validators; -public class GenderFieldValidator : AbstractValidator +public class GenderTypeValidator : AbstractValidator { - private const string ErrorCode = nameof(GenderFieldValidator); + private const string ErrorCode = nameof(GenderTypeValidator); + private readonly string _propertyName; + + public GenderTypeValidator(string propertyName) + { + this._propertyName = propertyName; + } /// /// return true 繼續往下驗證 @@ -24,12 +30,12 @@ protected override bool PreValidate(ValidationContext context, Validatio return isValid; } - var srcValues = GenderFieldValues.GetFieldValues(); + var srcValues = GenderTypeValues.GetFieldValues(); var destValue = instance.ToString(); if (srcValues.ContainsKey(destValue) == false) { - var validationFailure = new ValidationFailure("gender", - $"'{destValue}' is invalid value.") + var validationFailure = new ValidationFailure(this._propertyName, + $"'{destValue}' is invalid value.") { ErrorCode = ErrorCode }; diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameTypeValidator.cs similarity index 76% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameTypeValidator.cs index b99a212e..bd3914d3 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameFieldValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/NameTypeValidator.cs @@ -4,8 +4,15 @@ namespace Lab.DictionaryFluentValidation.Validators; -public class NameFieldValidator : AbstractValidator +public class NameTypeValidator : AbstractValidator { + private readonly string _propertyName; + + public NameTypeValidator(string propertyName) + { + this._propertyName = propertyName; + } + /// /// return true 繼續往下驗證 /// https://docs.fluentvalidation.net/en/latest/advanced.html?highlight=PreValidate#prevalidate @@ -31,14 +38,14 @@ protected override bool PreValidate(ValidationContext context, Validatio continue; } - var propertyName = $"name.{propertyInfo.Name}"; + var propertyName = $"{this._propertyName}.{propertyInfo.Name}"; switch (propertyInfo.Name) { - case NameFieldNames.FirstName: + case NameTypeNames.FirstName: break; - case NameFieldNames.LastName: + case NameTypeNames.LastName: break; - case NameFieldNames.FullName: + case NameTypeNames.FullName: this.RuleFor(p => value) .NotEmpty() .WithName(propertyName) diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs similarity index 70% rename from ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs rename to ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs index 492de546..6c857fd2 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs @@ -1,23 +1,18 @@ -using System.Linq.Expressions; using FluentValidation; using FluentValidation.Results; using Lab.DictionaryFluentValidation.Fields; namespace Lab.DictionaryFluentValidation.Validators; -public class ProfileValidator : AbstractValidator> +public class ProfileTypeValidator : AbstractValidator> { - private static readonly Lazy s_emailFieldValidatorLazy - = new(() => new EmailFieldValidator()); + private static EmailTypeValidator EmailTypeValidator => new(ProfileTypeNames.ContactEmail); - private static readonly Lazy s_nameFieldValidatorLazy - = new(() => new NameFieldValidator()); + private static NameTypeValidator NameTypeValidator => new(ProfileTypeNames.Name); - private static readonly Lazy s_birthdayFieldValidatorLazy - = new(() => new BirthdayFieldValidator()); + private static BirthdayTypeValidator BirthdayTypeValidator => new(ProfileTypeNames.Birthday); - private static readonly Lazy s_genderFieldValidatorLazy - = new(() => new GenderFieldValidator()); + private static GenderTypeValidator GenderTypeValidator => new(ProfileTypeNames.Gender); private static bool IsNotSupportFields(ValidationContext> context) { @@ -30,14 +25,14 @@ private static bool IsNotSupportFields(ValidationContext instances) { continue; } - + switch (fieldName) { - case ProfileFieldNames.ContactEmail: + case ProfileTypeNames.ContactEmail: { - this.RuleFor(p => p[fieldName]) - .SetValidator(p => s_emailFieldValidatorLazy.Value); - + var PropertyName = fieldName; + this.RuleFor(p => p[fieldName]) + .SetValidator(p => EmailTypeValidator) + ; + break; } - case ProfileFieldNames.Name: + case ProfileTypeNames.Name: { this.RuleFor(p => p[fieldName]) - .SetValidator(p => s_nameFieldValidatorLazy.Value) + .SetValidator(p => NameTypeValidator) ; break; } - case ProfileFieldNames.Birthday: + case ProfileTypeNames.Birthday: { this.RuleFor(p => p[fieldName]) - .SetValidator(p => s_birthdayFieldValidatorLazy.Value) + .SetValidator(p => BirthdayTypeValidator) ; break; } - case ProfileFieldNames.Gender: + case ProfileTypeNames.Gender: { this.RuleFor(p => p[fieldName]) - .SetValidator(p => s_genderFieldValidatorLazy.Value) + .SetValidator(p => GenderTypeValidator) ; break; } From a26eb70536d0055174ba37cbdd8f14e3eafbf8d0 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 12 Apr 2022 02:22:36 +0800 Subject: [PATCH 206/267] refactor --- .../ProfileValidatorTests.cs | 127 +++++++++++------- .../Validators/ProfileTypeValidator.cs | 29 ++-- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs index 51eb5067..af85a89c 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation.UnitTest/ProfileValidatorTests.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Lab.DictionaryFluentValidation.Fields; using Lab.DictionaryFluentValidation.Validators; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -10,20 +9,39 @@ namespace Lab.DictionaryFluentValidation.UnitTest; public class ProfileValidatorTests { [TestMethod] - public void 通過驗證() + public void aaaa() { - BirthdayTypeNames.GetFieldNames(); - BirthdayTypeNames.GetFieldNames(); + var profileValidator = new ProfileTypeValidator(); var data = new Dictionary { { "name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, { "birthday", new { year = 2000, month = 2, day = 28 } }, { "contactEmail", "yao@aa.bb" }, }; - - var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); - Assert.AreEqual(true, validationResult.IsValid); + + data = new Dictionary + { + { "gender", "公的" }, + }; + validationResult = profileValidator.Validate(data); + + data = new Dictionary + { + { "Name", null }, + }; + + validationResult = profileValidator.Validate(data); + data = new Dictionary + { + { "name", new { firstName = "yao", lastName = "", fullName = "" } }, + }; + validationResult = profileValidator.Validate(data); + data = new Dictionary + { + { "Hi", null }, + }; + validationResult = profileValidator.Validate(data); } [TestMethod] @@ -40,96 +58,96 @@ public void Key區分大小寫() var actualError = validationResult.Errors.First(); Assert.AreEqual("Name", actualError.PropertyName); Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); - Assert.AreEqual("not support column 'Name'", actualError.ErrorMessage); + Assert.AreEqual("'Name' column not support", actualError.ErrorMessage); } [TestMethod] - public void 必填欄位為空() + public void 二月三十是非法日期() { var data = new Dictionary { - { "name", new { firstName = "yao", lastName = "", fullName = "" } }, + { "birthday", new { year = 2000, month = 2, day = 30 } }, }; + var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); - - // Assert.AreEqual(false, validationResult.IsValid); + Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("name.fullName", actualError.PropertyName); - Assert.AreEqual("NotEmptyValidator", actualError.ErrorCode); - Assert.AreEqual("'name.fullName' must not be empty.", actualError.ErrorMessage); + Assert.AreEqual("birthday", actualError.PropertyName); + Assert.AreEqual(nameof(BirthdayTypeValidator), actualError.ErrorCode); + Assert.AreEqual("year:2000,month:2,day:30 is invalid date format", actualError.ErrorMessage); } [TestMethod] - public void 使用不支援的Key() + public void 日期內容為非法值() { var data = new Dictionary { - { "Hi", null }, + { "birthday", null }, }; var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); - Assert.AreEqual(false, validationResult.IsValid); - var actualError = validationResult.Errors.First(); - Assert.AreEqual("Hi", actualError.PropertyName); - Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); - Assert.AreEqual("not support column 'Hi'", actualError.ErrorMessage); + Assert.AreEqual(true, validationResult.IsValid); } [TestMethod] - public void 使用支援的Key() + public void 必填欄位為空() { var data = new Dictionary { - { "name", null }, + { "name", new { firstName = "yao", lastName = "", fullName = "" } }, }; - var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); - Assert.AreEqual(true, validationResult.IsValid); + + // Assert.AreEqual(false, validationResult.IsValid); + var actualError = validationResult.Errors.First(); + Assert.AreEqual("name.fullName", actualError.PropertyName); + Assert.AreEqual("NotEmptyValidator", actualError.ErrorCode); + Assert.AreEqual("'name.fullName' must not be empty.", actualError.ErrorMessage); } [TestMethod] - public void 二月三十是非法日期() + public void 沒有年是非法日期() { var data = new Dictionary { - { "birthday", new { year = 2000, month = 2, day = 30 } }, + { "birthday", new { month = 2, day = 30 } }, }; var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("birthday", actualError.PropertyName); + Assert.AreEqual("birthday.year", actualError.PropertyName); Assert.AreEqual(nameof(BirthdayTypeValidator), actualError.ErrorCode); - Assert.AreEqual("year:2000,month:2,day:30 is invalid date format", actualError.ErrorMessage); + Assert.AreEqual("'birthday.year' must not be empty.", actualError.ErrorMessage); } [TestMethod] - public void 沒有年是非法日期() + public void 使用不支援的Key() { var data = new Dictionary { - { "birthday", new { month = 2, day = 30 } }, + { "Hi", null }, }; var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("birthday.year", actualError.PropertyName); - Assert.AreEqual(nameof(BirthdayTypeValidator), actualError.ErrorCode); - Assert.AreEqual("'birthday.year' must not be empty.", actualError.ErrorMessage); + Assert.AreEqual("Hi", actualError.PropertyName); + Assert.AreEqual("NotSupportValidator", actualError.ErrorCode); + Assert.AreEqual("'Hi' column not support", actualError.ErrorMessage); } [TestMethod] - public void 日期內容為非法值() + public void 使用支援的Key() { var data = new Dictionary { - { "birthday", null }, + { "name", null }, }; var profileValidator = new ProfileTypeValidator(); @@ -138,34 +156,49 @@ public void 日期內容為非法值() } [TestMethod] - public void 郵件格式錯誤() + public void 性別格式錯誤() { var data = new Dictionary { - { "contactEmail", "yao" }, + { "gender", "公的" }, }; var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("contactEmail", actualError.PropertyName); - Assert.AreEqual(nameof(EmailTypeValidator), actualError.ErrorCode); - Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); + Assert.AreEqual("gender", actualError.PropertyName); + Assert.AreEqual(nameof(GenderTypeValidator), actualError.ErrorCode); + Assert.AreEqual("'公的' is invalid value.", actualError.ErrorMessage); } [TestMethod] - public void 性別格式錯誤() + public void 通過驗證() { var data = new Dictionary { - { "gender", "公的" }, + { "name", new { firstName = "yao", lastName = "yu", fullName = "yao-chang.yu" } }, + { "birthday", new { year = 2000, month = 2, day = 28 } }, + { "contactEmail", "yao@aa.bb" }, + }; + + var profileValidator = new ProfileTypeValidator(); + var validationResult = profileValidator.Validate(data); + Assert.AreEqual(true, validationResult.IsValid); + } + + [TestMethod] + public void 郵件格式錯誤() + { + var data = new Dictionary + { + { "contactEmail", "yao" }, }; var profileValidator = new ProfileTypeValidator(); var validationResult = profileValidator.Validate(data); Assert.AreEqual(false, validationResult.IsValid); var actualError = validationResult.Errors.First(); - Assert.AreEqual("gender", actualError.PropertyName); - Assert.AreEqual(nameof(GenderTypeValidator), actualError.ErrorCode); - Assert.AreEqual("'公的' is invalid value.", actualError.ErrorMessage); + Assert.AreEqual("contactEmail", actualError.PropertyName); + Assert.AreEqual(nameof(EmailTypeValidator), actualError.ErrorCode); + Assert.AreEqual("'contactEmail' is not a valid email address.", actualError.ErrorMessage); } } \ No newline at end of file diff --git a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs index 6c857fd2..7336f78c 100644 --- a/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs +++ b/ModelValidation/Lab.DictionaryValidation/Lab.DictionaryFluentValidation/Validators/ProfileTypeValidator.cs @@ -6,13 +6,25 @@ namespace Lab.DictionaryFluentValidation.Validators; public class ProfileTypeValidator : AbstractValidator> { - private static EmailTypeValidator EmailTypeValidator => new(ProfileTypeNames.ContactEmail); + private static readonly Lazy s_emailTypeValidatorLazy = + new(() => new EmailTypeValidator(ProfileTypeNames.ContactEmail)); - private static NameTypeValidator NameTypeValidator => new(ProfileTypeNames.Name); + private static readonly Lazy s_nameTypeValidator = + new Lazy(() => new NameTypeValidator(ProfileTypeNames.Name)); - private static BirthdayTypeValidator BirthdayTypeValidator => new(ProfileTypeNames.Birthday); + private static readonly Lazy s_birthdayTypeValidatorLazy = + new(() => new BirthdayTypeValidator(ProfileTypeNames.Birthday)); - private static GenderTypeValidator GenderTypeValidator => new(ProfileTypeNames.Gender); + private static readonly Lazy s_genderTypeValidatorLazy = + new(() => new GenderTypeValidator(ProfileTypeNames.Gender)); + + private static EmailTypeValidator EmailTypeValidator => s_emailTypeValidatorLazy.Value; + + private static NameTypeValidator NameTypeValidator => s_nameTypeValidator.Value; + + private static BirthdayTypeValidator BirthdayTypeValidator => s_birthdayTypeValidatorLazy.Value; + + private static GenderTypeValidator GenderTypeValidator => s_genderTypeValidatorLazy.Value; private static bool IsNotSupportFields(ValidationContext> context) { @@ -44,18 +56,18 @@ private static bool IsNotSupportFields(Dictionary sourceFields, string destFieldName, ValidationContext> context) { - var isSNotSupport = sourceFields.ContainsKey(destFieldName) == false; - if (isSNotSupport) + var isNotSupport = sourceFields.ContainsKey(destFieldName) == false; + if (isNotSupport) { var failure = new ValidationFailure(destFieldName, - $"not support column '{destFieldName}'") + $"'{destFieldName}' column not support") { ErrorCode = "NotSupportValidator", }; context.AddFailure(failure); } - return isSNotSupport; + return isNotSupport; } private static bool IsNotSupportNestFields(Dictionary sourceFields, @@ -106,7 +118,6 @@ private void SetValidateRule(Dictionary instances) { case ProfileTypeNames.ContactEmail: { - var PropertyName = fieldName; this.RuleFor(p => p[fieldName]) .SetValidator(p => EmailTypeValidator) ; From 94322d1977f286557249ce695807f1a8b5f6116d Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 15 Apr 2022 23:47:12 +0800 Subject: [PATCH 207/267] feat: add DictionaryStringObjectJsonConverter2 --- ...ctionaryStringObjectJsonConverter2Tests.cs | 243 ++++++++++++++++++ .../Lab.JsonConverterLib.UnitTest.csproj | 10 +- .../DictionaryStringObjectJsonConverter2.cs | 145 +++++++++++ 3 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs create mode 100644 Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter2.cs diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs new file mode 100644 index 00000000..8e882f2a --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.JsonConverterLib.UnitTest; + +[TestClass] +public class DictionaryStringObjectJsonConverter2Tests +{ + [TestMethod] + public void JsonDocument轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter2() } + }; + var expected = new Dictionary + { + // ["anonymousType"] = new Dictionary() + // { + // { "Prop", 123 } + // }, + // + // ["null"] = null!, + // ["model"] = new Dictionary() + // { + // { "Age", 19 }, + // { "Name", "小章" } + // }, + // ["dateTimeOffset"] = DateTimeOffset.Now, + // ["long"] = (long)255, + // ["decimal"] = (decimal)3.1416, + // ["guid"] = Guid.NewGuid(), + // ["string"] = "字串", + ["decimalArray"] = new List() { 1, (decimal)2.1 } + }; + var json = JsonSerializer.Serialize(expected); + + using var jsonDoc = json.ToJsonDocument(); + var actual = jsonDoc.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + + // Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void JsonDocument轉Dictionary1() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + using var jsonDoc = expected.ToJsonDocument(); + var actual = jsonDoc.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void JsonsNode轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + var json = JsonSerializer.Serialize(expected); + + var jsonObject = json.ToJsonNode(); + var actual = jsonObject.To>(options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void Memory轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var json = JsonSerializer.Serialize(expected, options); + var jsonMemory = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + var actual = JsonSerializer.Deserialize>(jsonMemory, options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void 字串轉Dictionary() + { + var options = new JsonSerializerOptions + { + Converters = { new DictionaryStringObjectJsonConverter() } + }; + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + + var json = JsonSerializer.Serialize(expected, options); + var actual = JsonSerializer.Deserialize>(json, options); + + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + [TestMethod] + public void 字串轉Dictionary_失敗() + { + var expected = new Dictionary + { + ["i"] = 255, + ["s"] = "字串", + ["d"] = new DateTime(1900, 1, 1), + ["a"] = new[] { 1, 2 }, + ["o"] = new { Prop = 123 } + }; + var json = JsonSerializer.Serialize(expected); + + var actual = JsonSerializer.Deserialize>(json); + Assert.AreNotEqual(expected["i"], actual["i"]); + Assert.AreNotEqual(expected["s"], actual["s"]); + + // 反序列化之後得到 JsonElement Type,必須要要透過其他手段才能取得真實的值 + Assert.AreEqual("JsonElement", actual["s"].GetType().Name); + Assert.AreEqual(expected["i"], ((JsonElement)actual["i"]).GetInt32()); + Assert.AreEqual(expected["s"], ((JsonElement)actual["s"]).GetString()); + } + + private static void AssertAnonymousType(Dictionary actual) + { + var expected = new Dictionary + { + { "Prop", (long)123 } + }; + + Assert.AreEqual(expected["Prop"], actual["Prop"]); + } + + private static void AssertDecimalArray(List actual) + { + var expected = new List + { + (long)1, + (decimal)2.1 + }; + + Assert.AreEqual(expected[0], actual[0]); + Assert.AreEqual(expected[1], actual[1]); + } + + private class Model + { + public string Name { get; set; } + + public int Age { get; set; } + } +} \ No newline at end of file diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj index d0b60f4b..3eb035e8 100644 --- a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj @@ -8,14 +8,14 @@ - - - - + + + + - + diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter2.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter2.cs new file mode 100644 index 00000000..9de6b135 --- /dev/null +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib/DictionaryStringObjectJsonConverter2.cs @@ -0,0 +1,145 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lab.JsonConverterLib; + +public class DictionaryStringObjectJsonConverter2 : JsonConverter> +{ + public override Dictionary Read(ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); + } + + var results = new Dictionary(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return results; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("JsonTokenType was not PropertyName"); + } + + var propertyName = reader.GetString(); + + if (string.IsNullOrWhiteSpace(propertyName)) + { + throw new JsonException("Failed to get property name"); + } + + reader.Read(); + + results.Add(propertyName, this.ReadValue(ref reader, options)); + } + + return results; + } + + public override void Write(Utf8JsonWriter writer, + Dictionary value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach (var key in value.Keys) + { + WriteValue(writer, key, value[key], options); + } + + writer.WriteEndObject(); + } + + private Dictionary ReadObjectValue(ref Utf8JsonReader reader, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException($"JsonTokenType was of type {reader.TokenType}, only objects are supported"); + } + + var results = new Dictionary(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return results; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("JsonTokenType was not PropertyName"); + } + + var propertyName = reader.GetString(); + + if (string.IsNullOrWhiteSpace(propertyName)) + { + throw new JsonException("Failed to get property name"); + } + + reader.Read(); + + results.Add(propertyName, this.ReadValue(ref reader, options)); + } + + return results; + } + + private object ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + if (reader.TryGetDateTimeOffset(out var dateOffset)) + { + return dateOffset; + } + + if (reader.TryGetGuid(out var guid)) + { + return guid; + } + + return reader.GetString(); + case JsonTokenType.False: + case JsonTokenType.True: + return reader.GetBoolean(); + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + return reader.GetDecimal(); + case JsonTokenType.StartObject: + return this.ReadObjectValue(ref reader, options); + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + list.Add(this.ReadValue(ref reader, options)); + } + + return list; + default: + throw new JsonException($"'{reader.TokenType}' is not supported"); + } + } + + private static void WriteValue(Utf8JsonWriter writer, + string key, + object value, + JsonSerializerOptions options) + { + if (key != null) + { + writer.WritePropertyName(key); + } + + JsonSerializer.Serialize(writer, value, options); + } +} \ No newline at end of file From e93914040acc0f1fb4b42f077d9a1b0b11a615a8 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 16 Apr 2022 20:56:28 +0800 Subject: [PATCH 208/267] feat: add test case --- ...ctionaryStringObjectJsonConverter2Tests.cs | 169 ++++-------------- ...ictionaryStringObjectJsonConverterTests.cs | 142 ++++----------- .../Lab.JsonConverterLib.UnitTest.csproj | 11 +- 3 files changed, 75 insertions(+), 247 deletions(-) diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs index 8e882f2a..12b8ebaf 100644 --- a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverter2Tests.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using System.Text.Json; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.JsonConverterLib.UnitTest; @@ -17,73 +18,12 @@ public void JsonDocument轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter2() } }; - var expected = new Dictionary - { - // ["anonymousType"] = new Dictionary() - // { - // { "Prop", 123 } - // }, - // - // ["null"] = null!, - // ["model"] = new Dictionary() - // { - // { "Age", 19 }, - // { "Name", "小章" } - // }, - // ["dateTimeOffset"] = DateTimeOffset.Now, - // ["long"] = (long)255, - // ["decimal"] = (decimal)3.1416, - // ["guid"] = Guid.NewGuid(), - // ["string"] = "字串", - ["decimalArray"] = new List() { 1, (decimal)2.1 } - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected); using var jsonDoc = json.ToJsonDocument(); var actual = jsonDoc.To>(options); - - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - - // Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); - } - - [TestMethod] - public void JsonDocument轉Dictionary1() - { - var options = new JsonSerializerOptions - { - Converters = { new DictionaryStringObjectJsonConverter() } - }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; - - using var jsonDoc = expected.ToJsonDocument(); - var actual = jsonDoc.To>(options); - - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(actual, expected); } [TestMethod] @@ -91,33 +31,25 @@ public void JsonsNode轉Dictionary() { var options = new JsonSerializerOptions { - Converters = { new DictionaryStringObjectJsonConverter() } - }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, + Converters = { new DictionaryStringObjectJsonConverter2() } }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected); var jsonObject = json.ToJsonNode(); var actual = jsonObject.To>(options); + AssertThat(actual, expected); + } + + private static void AssertThat(Dictionary actual, Dictionary expected) + { + actual["model"].Should().BeEquivalentTo(expected["model"]); + actual["decimalArray"].Should().BeEquivalentTo(expected["decimalArray"]); Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + Assert.AreEqual(expected["guid"], actual["guid"]); + Assert.AreEqual(expected["string"], actual["string"]); } [TestMethod] @@ -127,33 +59,14 @@ public void Memory轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter() } }; - - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected, options); var jsonMemory = new MemoryStream(Encoding.UTF8.GetBytes(json)); var actual = JsonSerializer.Deserialize>(jsonMemory, options); - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(actual, expected); } [TestMethod] @@ -163,30 +76,12 @@ public void 字串轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected, options); var actual = JsonSerializer.Deserialize>(json, options); - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(actual, expected); } [TestMethod] @@ -212,26 +107,22 @@ public void 字串轉Dictionary_失敗() Assert.AreEqual(expected["s"], ((JsonElement)actual["s"]).GetString()); } - private static void AssertAnonymousType(Dictionary actual) + private static Dictionary CreateDictionary() { var expected = new Dictionary { - { "Prop", (long)123 } - }; - - Assert.AreEqual(expected["Prop"], actual["Prop"]); - } - - private static void AssertDecimalArray(List actual) - { - var expected = new List - { - (long)1, - (decimal)2.1 + ["model"] = new Dictionary + { + { "Age", 19 }, + { "Name", "小章" } + }, + ["decimalArray"] = new List { 1, (decimal)2.1 }, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", }; - - Assert.AreEqual(expected[0], actual[0]); - Assert.AreEqual(expected[1], actual[1]); + return expected; } private class Model diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs index 48c87558..4cd6e579 100644 --- a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/DictionaryStringObjectJsonConverterTests.cs @@ -17,31 +17,13 @@ public void JsonDocument轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected); using var jsonDoc = json.ToJsonDocument(); var actual = jsonDoc.To>(options); - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(expected, actual); } [TestMethod] @@ -51,30 +33,11 @@ public void JsonDocument轉Dictionary1() { Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); using var jsonDoc = expected.ToJsonDocument(); var actual = jsonDoc.To>(options); - - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(expected, actual); } [TestMethod] @@ -84,31 +47,13 @@ public void JsonsNode轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected); var jsonObject = json.ToJsonNode(); var actual = jsonObject.To>(options); - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(expected, actual); } [TestMethod] @@ -119,32 +64,13 @@ public void Memory轉Dictionary() Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected, options); var jsonMemory = new MemoryStream(Encoding.UTF8.GetBytes(json)); var actual = JsonSerializer.Deserialize>(jsonMemory, options); - - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(expected, actual); } [TestMethod] @@ -154,30 +80,11 @@ public void 字串轉Dictionary() { Converters = { new DictionaryStringObjectJsonConverter() } }; - var expected = new Dictionary - { - ["anonymousType"] = new { Prop = 123 }, - ["model"] = new Model { Age = 19, Name = "小章" }, - ["null"] = null!, - ["dateTimeOffset"] = DateTimeOffset.Now, - ["long"] = (long)255, - ["decimal"] = (decimal)3.1416, - ["guid"] = Guid.NewGuid(), - ["string"] = "字串", - ["decimalArray"] = new[] { 1, (decimal)2.1 }, - }; + var expected = CreateDictionary(); var json = JsonSerializer.Serialize(expected, options); var actual = JsonSerializer.Deserialize>(json, options); - - Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); - Assert.AreEqual(expected["string"], actual["string"]); - Assert.AreEqual(expected["long"], actual["long"]); - Assert.AreEqual(expected["decimal"], actual["decimal"]); - Assert.AreEqual(expected["null"], actual["null"]); - - AssertAnonymousType(actual["anonymousType"] as Dictionary); - AssertDecimalArray(actual["decimalArray"] as List); + AssertThat(expected, actual); } [TestMethod] @@ -225,6 +132,35 @@ private static void AssertDecimalArray(List actual) Assert.AreEqual(expected[1], actual[1]); } + private static void AssertThat(Dictionary expected, Dictionary actual) + { + Assert.AreEqual(expected["dateTimeOffset"], actual["dateTimeOffset"]); + Assert.AreEqual(expected["string"], actual["string"]); + Assert.AreEqual(expected["long"], actual["long"]); + Assert.AreEqual(expected["decimal"], actual["decimal"]); + Assert.AreEqual(expected["null"], actual["null"]); + + AssertAnonymousType(actual["anonymousType"] as Dictionary); + AssertDecimalArray(actual["decimalArray"] as List); + } + + private static Dictionary CreateDictionary() + { + var expected = new Dictionary + { + ["anonymousType"] = new { Prop = 123 }, + ["model"] = new Model { Age = 19, Name = "小章" }, + ["null"] = null!, + ["dateTimeOffset"] = DateTimeOffset.Now, + ["long"] = (long)255, + ["decimal"] = (decimal)3.1416, + ["guid"] = Guid.NewGuid(), + ["string"] = "字串", + ["decimalArray"] = new[] { 1, (decimal)2.1 }, + }; + return expected; + } + private class Model { public string Name { get; set; } diff --git a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj index 3eb035e8..ec415e09 100644 --- a/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj +++ b/Json/Lab.JsonConverter/Lab.JsonConverterLib.UnitTest/Lab.JsonConverterLib.UnitTest.csproj @@ -8,14 +8,15 @@ - - - - + + + + + - + From 6082108d8425a2cda16700e55df8d39d72588963 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 14 May 2022 17:36:40 +0800 Subject: [PATCH 209/267] =?UTF-8?q?fea:=20=E6=96=B0=E5=A2=9E=20System.Text?= =?UTF-8?q?.Json.JsonDiffPatch=20=E6=B8=AC=E8=A9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lab.Json.UnitTest.csproj | 17 +++ .../Lab.Json.UnitTest/UnitTest1.cs | 12 ++ .../Lab.JsonCompare.UnitTest.csproj | 18 +++ .../SystemTextJsonTests.cs | 137 ++++++++++++++++++ Json/Lab.JsonCompare/Lab.JsonCompare.sln | 16 ++ 5 files changed, 200 insertions(+) create mode 100644 Json/Lab.JsonCompare/Lab.Json.UnitTest/Lab.Json.UnitTest.csproj create mode 100644 Json/Lab.JsonCompare/Lab.Json.UnitTest/UnitTest1.cs create mode 100644 Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj create mode 100644 Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs create mode 100644 Json/Lab.JsonCompare/Lab.JsonCompare.sln diff --git a/Json/Lab.JsonCompare/Lab.Json.UnitTest/Lab.Json.UnitTest.csproj b/Json/Lab.JsonCompare/Lab.Json.UnitTest/Lab.Json.UnitTest.csproj new file mode 100644 index 00000000..c83c50db --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.Json.UnitTest/Lab.Json.UnitTest.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + + false + + + + + + + + + + diff --git a/Json/Lab.JsonCompare/Lab.Json.UnitTest/UnitTest1.cs b/Json/Lab.JsonCompare/Lab.Json.UnitTest/UnitTest1.cs new file mode 100644 index 00000000..9d646e0a --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.Json.UnitTest/UnitTest1.cs @@ -0,0 +1,12 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.Json.UnitTest; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod1() + { + } +} \ No newline at end of file diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj new file mode 100644 index 00000000..74a9fc61 --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs new file mode 100644 index 00000000..5a15b6d0 --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs @@ -0,0 +1,137 @@ +using System; +using System.Text.Json; +using System.Text.Json.JsonDiffPatch; +using System.Text.Json.JsonDiffPatch.MsTest; +using System.Text.Json.Nodes; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.JsonCompare.UnitTest; + +[TestClass] +public class SystemTextJsonTests +{ + [TestMethod] + public void 比對兩個一樣的Json字串_via_JsonDiffPatcher() + { + var o1 = new JsonObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2) } + }; + + var o2 = new JsonObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2) } + }; + + var left = o1.ToJsonString(); + var right = o2.ToJsonString(); + var diff = JsonDiffPatcher.Diff(left, right); + if (diff != null) + { + Console.WriteLine(JsonSerializer.Serialize(diff)); + } + + Assert.IsNull(diff); + } + + [TestMethod] + public void 比對兩個不一樣的JsonObject() + { + var o1 = new JsonObject + { + { "Integer", 12345 }, + { "String", JsonValue.Create("A string") }, + { "Items", new JsonArray(1, 2) } + }; + + var o2 = new JsonObject + { + { "integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } + }; + + var diff = o1.Diff(o2); + if (diff != null) + { + Console.WriteLine(JsonSerializer.Serialize(diff)); + } + + Assert.IsNotNull(diff); + } + + [TestMethod] + public void 比對兩個不一樣的JsonNode() + { + var o1 = new JsonObject + { + { "Integer", 12345 }, + { "String", JsonValue.Create("A string") }, + { "Items", new JsonArray(1, 2) } + }; + + var o2 = new JsonObject + { + { "integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } + }; + + var left = JsonNode.Parse(o1.ToJsonString()); + var right = JsonNode.Parse(o2.ToJsonString()); + var diff = left.Diff(right); + if (diff != null) + { + Console.WriteLine(JsonSerializer.Serialize(diff)); + } + + Assert.IsNotNull(diff); + } + + [TestMethod] + public void 比對兩個不一樣的JsonObject_via_JsonAssert() + { + var o1 = new JsonObject + { + { "Integer", 12345 }, + { "String", JsonValue.Create("A string") }, + { "Items", new JsonArray(1, 2) } + }; + + var o2 = new JsonObject + { + { "integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } + }; + Assert.That.JsonAreEqual(o1, o2, true); + } + + [TestMethod] + public void 比對兩個不一樣的JsonDocument() + { + var o1 = new JsonObject + { + { "Integer", 12345 }, + { "String", JsonValue.Create("A string") }, + { "Items", new JsonArray(1, 2) } + }; + + var o2 = new JsonObject + { + { "integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } + }; + + var left = JsonDocument.Parse(o1.ToJsonString()); + var right = JsonDocument.Parse(o2.ToJsonString()); + + var isEquals = left.DeepEquals(right); + Assert.IsFalse(isEquals); + } +} \ No newline at end of file diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.sln b/Json/Lab.JsonCompare/Lab.JsonCompare.sln new file mode 100644 index 00000000..77f11d88 --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.JsonCompare.UnitTest", "Lab.JsonCompare.UnitTest\Lab.JsonCompare.UnitTest.csproj", "{D79E4743-D20C-4712-8E8E-F0323D05A339}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D79E4743-D20C-4712-8E8E-F0323D05A339}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D79E4743-D20C-4712-8E8E-F0323D05A339}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D79E4743-D20C-4712-8E8E-F0323D05A339}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D79E4743-D20C-4712-8E8E-F0323D05A339}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 2c9f5f2791861161194aabc56ab7938d6e71a16c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 May 2022 09:37:19 +0000 Subject: [PATCH 210/267] build(deps): bump CefSharp.WinForms in /CEF/Lab.Startup/WinFormNet48 Bumps [CefSharp.WinForms](https://github.com/cefsharp/cefsharp) from 79.1.360 to 98.1.210. - [Release notes](https://github.com/cefsharp/cefsharp/releases) - [Commits](https://github.com/cefsharp/cefsharp/compare/v79.1.360...v98.1.210) --- updated-dependencies: - dependency-name: CefSharp.WinForms dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- CEF/Lab.Startup/WinFormNet48/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CEF/Lab.Startup/WinFormNet48/packages.config b/CEF/Lab.Startup/WinFormNet48/packages.config index 7ad8ca9f..db8da869 100644 --- a/CEF/Lab.Startup/WinFormNet48/packages.config +++ b/CEF/Lab.Startup/WinFormNet48/packages.config @@ -4,7 +4,7 @@ - + \ No newline at end of file From 7f7396ab630b8c72efacd6c6612845ebf94e884e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 May 2022 09:37:21 +0000 Subject: [PATCH 211/267] build(deps): bump CefSharp.WinForms in /CEF/Lab.Startup/WinFormCore30 Bumps [CefSharp.WinForms](https://github.com/cefsharp/cefsharp) from 79.1.360 to 98.1.210. - [Release notes](https://github.com/cefsharp/cefsharp/releases) - [Commits](https://github.com/cefsharp/cefsharp/compare/v79.1.360...v98.1.210) --- updated-dependencies: - dependency-name: CefSharp.WinForms dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- CEF/Lab.Startup/WinFormCore30/WinFormCore30.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CEF/Lab.Startup/WinFormCore30/WinFormCore30.csproj b/CEF/Lab.Startup/WinFormCore30/WinFormCore30.csproj index ae5a740d..755aa470 100644 --- a/CEF/Lab.Startup/WinFormCore30/WinFormCore30.csproj +++ b/CEF/Lab.Startup/WinFormCore30/WinFormCore30.csproj @@ -18,7 +18,7 @@ - + From 585697a13917514485e853e9aa8fec1cbc7b3f34 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 14 May 2022 20:02:43 +0800 Subject: [PATCH 212/267] feat: add NewtonsoftJsonDiffPathTests --- .../Lab.JsonCompare.UnitTest.csproj | 11 ++-- .../NewtonsoftJsonDiffPathTests.cs | 57 +++++++++++++++++++ ...ests.cs => SystemTextJsonDiffPathTests.cs} | 2 +- 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs rename Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/{SystemTextJsonTests.cs => SystemTextJsonDiffPathTests.cs} (98%) diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj index 74a9fc61..fbe32aae 100644 --- a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/Lab.JsonCompare.UnitTest.csproj @@ -8,11 +8,12 @@ - - - - - + + + + + + diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs new file mode 100644 index 00000000..5585fd1a --- /dev/null +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs @@ -0,0 +1,57 @@ +using System; +using JsonDiffPatchDotNet; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json.Linq; + +namespace Lab.JsonCompare.UnitTest; + +[TestClass] +public class NewtonsoftJsonDiffPathTests +{ + [TestMethod] + public void 比對兩個一樣的JObject() + { + JObject o1 = new JObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JArray(1, 2) } + }; + + JObject o2 = new JObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JArray(1, 2) } + }; + var isEquals = JToken.DeepEquals(o1, o2); + Assert.IsTrue(isEquals); + } + + [TestMethod] + public void 比對兩個不一樣的JObject() + { + var o1 = new JObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JArray(1, 2) } + }; + + var o2 = new JObject + { + { "integer", 12345 }, + { "String", "A string" }, + { "Items", new JArray(1, 2, new JArray { "a", "b" }) } + }; + var diffPath = new JsonDiffPatch(); + var diff = diffPath.Diff(o1, o2); + + if (diff != null) + { + Console.WriteLine(diff.ToString()); + } + + Assert.IsNotNull(diff); + } +} \ No newline at end of file diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs similarity index 98% rename from Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs rename to Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs index 5a15b6d0..c2074020 100644 --- a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonTests.cs +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs @@ -8,7 +8,7 @@ namespace Lab.JsonCompare.UnitTest; [TestClass] -public class SystemTextJsonTests +public class SystemTextJsonDiffPathTests { [TestMethod] public void 比對兩個一樣的Json字串_via_JsonDiffPatcher() From c136d927b6817855271a26d16d3ab997489685f1 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 14 May 2022 21:59:00 +0800 Subject: [PATCH 213/267] refactor --- .../NewtonsoftJsonDiffPathTests.cs | 12 ++-- .../SystemTextJsonDiffPathTests.cs | 59 +++++++++++++------ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs index 5585fd1a..4a12ad38 100644 --- a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/NewtonsoftJsonDiffPathTests.cs @@ -11,41 +11,41 @@ public class NewtonsoftJsonDiffPathTests [TestMethod] public void 比對兩個一樣的JObject() { - JObject o1 = new JObject + var source = new JObject { { "Integer", 12345 }, { "String", "A string" }, { "Items", new JArray(1, 2) } }; - JObject o2 = new JObject + var dest = new JObject { { "Integer", 12345 }, { "String", "A string" }, { "Items", new JArray(1, 2) } }; - var isEquals = JToken.DeepEquals(o1, o2); + var isEquals = JToken.DeepEquals(source, dest); Assert.IsTrue(isEquals); } [TestMethod] public void 比對兩個不一樣的JObject() { - var o1 = new JObject + var source = new JObject { { "Integer", 12345 }, { "String", "A string" }, { "Items", new JArray(1, 2) } }; - var o2 = new JObject + var dest = new JObject { { "integer", 12345 }, { "String", "A string" }, { "Items", new JArray(1, 2, new JArray { "a", "b" }) } }; var diffPath = new JsonDiffPatch(); - var diff = diffPath.Diff(o1, o2); + var diff = diffPath.Diff(source, dest); if (diff != null) { diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs index c2074020..9218117e 100644 --- a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs @@ -13,22 +13,22 @@ public class SystemTextJsonDiffPathTests [TestMethod] public void 比對兩個一樣的Json字串_via_JsonDiffPatcher() { - var o1 = new JsonObject + var source = new JsonObject { { "Integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2) } }; - var o2 = new JsonObject + var dest = new JsonObject { { "Integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2) } }; - var left = o1.ToJsonString(); - var right = o2.ToJsonString(); + var left = source.ToJsonString(); + var right = dest.ToJsonString(); var diff = JsonDiffPatcher.Diff(left, right); if (diff != null) { @@ -41,21 +41,21 @@ public void 比對兩個一樣的Json字串_via_JsonDiffPatcher() [TestMethod] public void 比對兩個不一樣的JsonObject() { - var o1 = new JsonObject + var source = new JsonObject { { "Integer", 12345 }, { "String", JsonValue.Create("A string") }, { "Items", new JsonArray(1, 2) } }; - var o2 = new JsonObject + var dest = new JsonObject { { "integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } }; - var diff = o1.Diff(o2); + var diff = source.Diff(dest); if (diff != null) { Console.WriteLine(JsonSerializer.Serialize(diff)); @@ -67,22 +67,22 @@ public void 比對兩個不一樣的JsonObject() [TestMethod] public void 比對兩個不一樣的JsonNode() { - var o1 = new JsonObject + var source = new JsonObject { { "Integer", 12345 }, { "String", JsonValue.Create("A string") }, { "Items", new JsonArray(1, 2) } }; - var o2 = new JsonObject + var dest = new JsonObject { { "integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } }; - var left = JsonNode.Parse(o1.ToJsonString()); - var right = JsonNode.Parse(o2.ToJsonString()); + var left = JsonNode.Parse(source.ToJsonString()); + var right = JsonNode.Parse(dest.ToJsonString()); var diff = left.Diff(right); if (diff != null) { @@ -95,43 +95,66 @@ public void 比對兩個不一樣的JsonNode() [TestMethod] public void 比對兩個不一樣的JsonObject_via_JsonAssert() { - var o1 = new JsonObject + var source = new JsonObject { { "Integer", 12345 }, { "String", JsonValue.Create("A string") }, { "Items", new JsonArray(1, 2) } }; - var o2 = new JsonObject + var dest = new JsonObject { { "integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } }; - Assert.That.JsonAreEqual(o1, o2, true); + Assert.That.JsonAreEqual(source, dest, true); } [TestMethod] public void 比對兩個不一樣的JsonDocument() { - var o1 = new JsonObject + var source = new JsonObject { { "Integer", 12345 }, { "String", JsonValue.Create("A string") }, { "Items", new JsonArray(1, 2) } }; - var o2 = new JsonObject + var dest = new JsonObject { { "integer", 12345 }, { "String", "A string" }, { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } }; - var left = JsonDocument.Parse(o1.ToJsonString()); - var right = JsonDocument.Parse(o2.ToJsonString()); + var left = JsonDocument.Parse(source.ToJsonString()); + var right = JsonDocument.Parse(dest.ToJsonString()); var isEquals = left.DeepEquals(right); Assert.IsFalse(isEquals); } + + [TestMethod] + public void 更新節點() + { + var source = new JsonObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } + }; + + var dest = new JsonObject + { + { "Integer", 12345 }, + { "String", "A string" }, + { "Items", new JsonArray(1, 2) } + }; + + var diff = source.Diff(dest); + JsonDiffPatcher.Patch(ref diff, dest); + + Assert.That.JsonAreEqual(source,dest); + } } \ No newline at end of file From d0895a8a2669b65e9d5f13671245e6e196858d90 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 15 May 2022 02:01:45 +0800 Subject: [PATCH 214/267] add json patch test case --- .../SystemTextJsonDiffPathTests.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs index 9218117e..32259e38 100644 --- a/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs +++ b/Json/Lab.JsonCompare/Lab.JsonCompare.UnitTest/SystemTextJsonDiffPathTests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.JsonDiffPatch; using System.Text.Json.JsonDiffPatch.MsTest; @@ -123,8 +125,8 @@ public void 比對兩個不一樣的JsonDocument() var dest = new JsonObject { - { "integer", 12345 }, - { "String", "A string" }, + { "Integer", 12345 }, + { "String", JsonValue.Create("A string") }, { "Items", new JsonArray(1, 2, new JsonArray { "a", "b" }) } }; @@ -151,10 +153,14 @@ public void 更新節點() { "String", "A string" }, { "Items", new JsonArray(1, 2) } }; + + var left = JsonNode.Parse(dest.ToJsonString()); + var right = JsonNode.Parse(source.ToJsonString()); + + //左邊不等於來源,跟我認知的不一樣 + var diff = left.Diff(right); + JsonDiffPatcher.Patch(ref left, diff); - var diff = source.Diff(dest); - JsonDiffPatcher.Patch(ref diff, dest); - - Assert.That.JsonAreEqual(source,dest); + Assert.That.JsonAreEqual(right, right, true); } } \ No newline at end of file From 07b74a6251c362b3752948a817f7d63d77cd35aa Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 13:25:20 +0800 Subject: [PATCH 215/267] feat: add project --- ...sicAuthenticationSite.IntegrateTest.csproj | 21 +++++ .../TestServer.cs | 18 ++++ .../UnitTest1.cs | 19 ++++ .../Controllers/DemoController.cs | 22 +++++ .../Controllers/ProtectController.cs | 21 +++++ ...re.Security.BasicAuthenticationSite.csproj | 22 +++++ .../Program.cs | 30 +++++++ .../Properties/launchSettings.json | 31 +++++++ .../BasicAuthenticationDefaults.cs | 6 ++ .../BasicAuthenticationExtensions.cs | 39 ++++++++ .../BasicAuthenticationHandler.cs | 89 +++++++++++++++++++ .../BasicAuthenticationOptions.cs | 8 ++ ...BasicAuthenticationPostConfigureOptions.cs | 14 +++ .../BasicAuthenticationProvider.cs | 9 ++ .../IBasicAuthenticationProvider.cs | 6 ++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 ++ .../Lab.AspNetCore.Security.sln | 22 +++++ 18 files changed, 394 insertions(+) create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Properties/launchSettings.json create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationDefaults.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationPostConfigureOptions.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.Development.json create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.json create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj new file mode 100644 index 00000000..90db6373 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs new file mode 100644 index 00000000..9330cf99 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; + +public class TestServer : WebApplicationFactory +{ + private void ConfigureServices(IServiceCollection services) + { + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(this.ConfigureServices); + builder.UseSetting("https_port", "9527"); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs new file mode 100644 index 00000000..2ea722f9 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void 訪問沒有授權的服務() + { + var server = new TestServer(); + var httpClient = server.CreateClient(); + var url = "demo"; + var response = httpClient.GetAsync(url).Result; + var result = response.Content.ReadAsStringAsync().Result; + Console.WriteLine(result); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs new file mode 100644 index 00000000..674df575 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; + +[ApiController] +[Route("[controller]")] +public class DemoController : ControllerBase +{ + private readonly ILogger _logger; + + public DemoController(ILogger logger) + { + this._logger = logger; + } + + public async Task Get() + { + this._logger.LogDebug("訪問沒有授權的端點"); + Console.WriteLine("AAAAA"); + return this.Ok(); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs new file mode 100644 index 00000000..b679085a --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; + +[ApiController] +[Route("[controller]")] +public class ProtectController : ControllerBase +{ + private readonly ILogger _logger; + + public ProtectController(ILogger logger) + { + this._logger = logger; + } + + // [HttpGet(Name = "GetWeatherForecast")] + public async Task Get() + { + return this.Ok(); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj new file mode 100644 index 00000000..b4a1e621 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs new file mode 100644 index 00000000..bc7e56af --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -0,0 +1,30 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Logging.AddConsole(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); + +public partial class Program +{ +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Properties/launchSettings.json b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Properties/launchSettings.json new file mode 100644 index 00000000..4ed84fd8 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20169", + "sslPort": 44329 + } + }, + "profiles": { + "Lab.AspNetCore.Security.BasicAuthenticationSite": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7089;http://localhost:5089", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationDefaults.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationDefaults.cs new file mode 100644 index 00000000..ccd37f3f --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationDefaults.cs @@ -0,0 +1,6 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public static class BasicAuthenticationDefaults +{ + public const string AuthenticationScheme = "Basic"; +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs new file mode 100644 index 00000000..d01902b6 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Options; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public static class BasicAuthenticationExtensions +{ + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); + } + + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, + string authenticationScheme) + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, authenticationScheme, _ => { }); + } + + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, + Action configureOptions) + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); + } + + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, + string authenticationScheme, Action configureOptions) + where TAuthService : class, IBasicAuthenticationProvider + { + builder.Services + .AddSingleton, BasicAuthenticationPostConfigureOptions>(); + builder.Services.AddTransient(); + + return builder.AddScheme( + authenticationScheme, configureOptions); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs new file mode 100644 index 00000000..1ae4fcae --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -0,0 +1,89 @@ +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Options; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public class BasicAuthenticationHandler : AuthenticationHandler +{ + private const string AuthorizationHeaderName = "Authorization"; + private const string BasicSchemeName = "Basic"; + private readonly IBasicAuthenticationProvider _authenticationProvider; + + public BasicAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + IBasicAuthenticationProvider authenticationService) + : base(options, logger, encoder, clock) + { + this._authenticationProvider = authenticationService; + } + + protected override async Task HandleAuthenticateAsync() + { + if (!this.Request.Headers.ContainsKey(AuthorizationHeaderName)) + { + // return AuthenticateResult.Fail("Invalid Basic authentication header"); + //Authorization header not in request + return AuthenticateResult.NoResult(); + } + + if (!AuthenticationHeaderValue.TryParse(this.Request.Headers[AuthorizationHeaderName], + out var headerValue)) + { + //Invalid Authorization header + return AuthenticateResult.NoResult(); + } + + if (headerValue.Scheme.StartsWith(BasicSchemeName, StringComparison.InvariantCultureIgnoreCase) == false) + { + return AuthenticateResult.NoResult(); + } + + if (!BasicSchemeName.Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase)) + { + //Not Basic authentication header + return AuthenticateResult.NoResult(); + } + + var headerValueBytes = Convert.FromBase64String(headerValue.Parameter); + var userAndPassword = Encoding.UTF8.GetString(headerValueBytes); + var parts = userAndPassword.Split(':'); + if (parts.Length != 2) + { + return AuthenticateResult.Fail("Invalid Basic authentication header"); + } + + var user = parts[0]; + var password = parts[1]; + + var isValidUser = await this._authenticationProvider.IsValidUserAsync(user, password); + + if (!isValidUser) + { + return AuthenticateResult.Fail("Invalid username or password"); + } + + return this.SignIn(user); + } + + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) + { + this.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{this.Options.Realm}\", charset=\"UTF-8\""; + await base.HandleChallengeAsync(properties); + } + + private AuthenticateResult SignIn(string user) + { + var claims = new[] { new Claim(ClaimTypes.Name, user) }; + var identity = new ClaimsIdentity(claims, this.Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, this.Scheme.Name); + return AuthenticateResult.Success(ticket); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs new file mode 100644 index 00000000..e40c62d7 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Authentication; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public class BasicAuthenticationOptions : AuthenticationSchemeOptions +{ + public string Realm { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationPostConfigureOptions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationPostConfigureOptions.cs new file mode 100644 index 00000000..40c7a136 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationPostConfigureOptions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Options; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public class BasicAuthenticationPostConfigureOptions : IPostConfigureOptions +{ + public void PostConfigure(string name, BasicAuthenticationOptions options) + { + if (string.IsNullOrEmpty(options.Realm)) + { + throw new InvalidOperationException("Realm must be provided in options"); + } + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs new file mode 100644 index 00000000..eee8b35d --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs @@ -0,0 +1,9 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public class BasicAuthenticationProvider : IBasicAuthenticationProvider +{ + public Task IsValidUserAsync(string user, string password) + { + return Task.FromResult(true); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs new file mode 100644 index 00000000..ef68a3a5 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs @@ -0,0 +1,6 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public interface IBasicAuthenticationProvider +{ + Task IsValidUserAsync(string user, string password); +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.Development.json b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.json b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln new file mode 100644 index 00000000..1d518087 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCore.Security.BasicAuthenticationSite", "Lab.AspNetCore.Security.BasicAuthenticationSite\Lab.AspNetCore.Security.BasicAuthenticationSite.csproj", "{C2FC8F88-DAD8-4677-8C5A-A9353C5353D7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest", "Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest\Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj", "{13085C3E-F174-45D2-B8F7-3EE51D42DDF4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C2FC8F88-DAD8-4677-8C5A-A9353C5353D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2FC8F88-DAD8-4677-8C5A-A9353C5353D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2FC8F88-DAD8-4677-8C5A-A9353C5353D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2FC8F88-DAD8-4677-8C5A-A9353C5353D7}.Release|Any CPU.Build.0 = Release|Any CPU + {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From b67c3afd2f518d017b5d987f427d74851fc03f25 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 19:15:17 +0800 Subject: [PATCH 216/267] feat: add test case --- .../TestServer.cs | 7 +++- .../UnitTest1.cs | 41 +++++++++++++++++++ .../Controllers/DemoController.cs | 4 +- .../Controllers/ProtectController.cs | 21 ---------- .../Controllers/User.cs | 8 ++++ .../Controllers/UserController.cs | 27 ++++++++++++ .../Program.cs | 9 ++++ .../BasicAuthenticationHandler.cs | 16 +++----- .../BasicAuthenticationOptions.cs | 2 +- .../BasicAuthenticationProvider.cs | 17 +++++++- .../IBasicAuthenticationProvider.cs | 2 +- 11 files changed, 114 insertions(+), 40 deletions(-) delete mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs index 9330cf99..3ddc0f95 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs @@ -12,7 +12,10 @@ private void ConfigureServices(IServiceCollection services) protected override void ConfigureWebHost(IWebHostBuilder builder) { - builder.ConfigureServices(this.ConfigureServices); - builder.UseSetting("https_port", "9527"); + builder.ConfigureServices(this.ConfigureServices) + .UseSetting("https_port", "9527") + + // .UseUrls("https://localhost:9527") + ; } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs index 2ea722f9..e4400bd3 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs @@ -1,4 +1,8 @@ using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; @@ -16,4 +20,41 @@ public void 訪問沒有授權的服務() var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); } + + [TestMethod] + public void 訪問受保護的服務() + { + var server = new TestServer(); + var httpClient = server.CreateClient(); + var url = "user"; + var clientId = "YAO"; + var clientSecret = "9527"; + using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); + var response = httpClient.SendAsync(requestMessage).Result; + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + + [TestMethod] + public void 訪問受保護的服務_驗證失敗() + { + var server = new TestServer(); + var httpClient = server.CreateClient(); + var url = "user"; + var clientId = "YAO1234"; + var clientSecret = "9527"; + using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); + var response = httpClient.SendAsync(requestMessage).Result; + response.Headers.TryGetValues("WWW-Authenticate", out var result); + Console.WriteLine($"驗證失敗:{result}"); + Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); + } + + private static HttpRequestMessage CreateBasicAuthenticationRequest(string url, string clientId, string clientSecret) + { + var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + var authenticationString = $"{clientId}:{clientSecret}"; + var base64Encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes(authenticationString)); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("basic", base64Encoded); + return requestMessage; + } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs index 674df575..0a598033 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs @@ -15,8 +15,6 @@ public DemoController(ILogger logger) public async Task Get() { - this._logger.LogDebug("訪問沒有授權的端點"); - Console.WriteLine("AAAAA"); - return this.Ok(); + return this.Ok("好"); } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs deleted file mode 100644 index b679085a..00000000 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/ProtectController.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; - -[ApiController] -[Route("[controller]")] -public class ProtectController : ControllerBase -{ - private readonly ILogger _logger; - - public ProtectController(ILogger logger) - { - this._logger = logger; - } - - // [HttpGet(Name = "GetWeatherForecast")] - public async Task Get() - { - return this.Ok(); - } -} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs new file mode 100644 index 00000000..acde4a03 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs @@ -0,0 +1,8 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; + +internal class User +{ + public string Name { get; set; } + + public int Age { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs new file mode 100644 index 00000000..695aa74a --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; + +[ApiController] +[Route("[controller]")] +public class UserController : ControllerBase +{ + private readonly ILogger _logger; + + public UserController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet] + [Authorize] + public async Task Get() + { + return this.Ok(new User + { + Name = this.User.Identity.Name, + Age = 18 + }); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index bc7e56af..f6fb3d9d 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -1,3 +1,5 @@ +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -8,6 +10,12 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Logging.AddConsole(); +builder.Services.AddAuthentication("Basic") + .AddScheme("Basic", null); +builder.Services.AddSingleton(); + +// builder.Services.AddSingleton(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -19,6 +27,7 @@ app.UseHttpsRedirection(); +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index 1ae4fcae..b10e244c 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -10,7 +10,7 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public class BasicAuthenticationHandler : AuthenticationHandler { private const string AuthorizationHeaderName = "Authorization"; - private const string BasicSchemeName = "Basic"; + private const string BasicSchemeName = BasicAuthenticationDefaults.AuthenticationScheme; private readonly IBasicAuthenticationProvider _authenticationProvider; public BasicAuthenticationHandler( @@ -18,10 +18,10 @@ public BasicAuthenticationHandler( ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, - IBasicAuthenticationProvider authenticationService) + IBasicAuthenticationProvider authenticationProvider) : base(options, logger, encoder, clock) { - this._authenticationProvider = authenticationService; + this._authenticationProvider = authenticationProvider; } protected override async Task HandleAuthenticateAsync() @@ -45,12 +45,6 @@ protected override async Task HandleAuthenticateAsync() return AuthenticateResult.NoResult(); } - if (!BasicSchemeName.Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase)) - { - //Not Basic authentication header - return AuthenticateResult.NoResult(); - } - var headerValueBytes = Convert.FromBase64String(headerValue.Parameter); var userAndPassword = Encoding.UTF8.GetString(headerValueBytes); var parts = userAndPassword.Split(':'); @@ -62,9 +56,9 @@ protected override async Task HandleAuthenticateAsync() var user = parts[0]; var password = parts[1]; - var isValidUser = await this._authenticationProvider.IsValidUserAsync(user, password); + var isValidate = await this._authenticationProvider.IsValidateAsync(user, password, CancellationToken.None); - if (!isValidUser) + if (!isValidate) { return AuthenticateResult.Fail("Invalid username or password"); } diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs index e40c62d7..735e435f 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationOptions.cs @@ -4,5 +4,5 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public class BasicAuthenticationOptions : AuthenticationSchemeOptions { - public string Realm { get; set; } + public string Realm { get; set; } = "Demo Site"; } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs index eee8b35d..38de0579 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationProvider.cs @@ -2,8 +2,23 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public class BasicAuthenticationProvider : IBasicAuthenticationProvider { - public Task IsValidUserAsync(string user, string password) + private readonly Dictionary _clientIdentities = new(StringComparer.InvariantCultureIgnoreCase) { + { "yao", "9527" } + }; + + public Task IsValidateAsync(string user, string password, CancellationToken cancel = default) + { + if (this._clientIdentities.TryGetValue(user, out var secret) == false) + { + return Task.FromResult(false); + } + + if (password != secret) + { + return Task.FromResult(false); + } + return Task.FromResult(true); } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs index ef68a3a5..a5c4d00a 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/IBasicAuthenticationProvider.cs @@ -2,5 +2,5 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public interface IBasicAuthenticationProvider { - Task IsValidUserAsync(string user, string password); + Task IsValidateAsync(string user, string password, CancellationToken cancel); } \ No newline at end of file From 99627a32ee217cb077d0e6109e93291cf4a2dd56 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 20:19:15 +0800 Subject: [PATCH 217/267] =?UTF-8?q?feat:=20=E8=A8=AA=E5=95=8F=E4=B8=8D?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E6=8E=88=E6=AC=8A=E7=9A=84=E6=9C=8D=E5=8B=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UnitTest1.cs | 4 +- .../Controllers/DemoController.cs | 2 + .../Program.cs | 4 +- .../BasicAuthenticationHandler.cs | 41 +++++++++++-------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs index e4400bd3..fcd14617 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs @@ -11,7 +11,7 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; public class UnitTest1 { [TestMethod] - public void 訪問沒有授權的服務() + public void 訪問不需要授權的服務() { var server = new TestServer(); var httpClient = server.CreateClient(); @@ -19,6 +19,8 @@ public void 訪問沒有授權的服務() var response = httpClient.GetAsync(url).Result; var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } [TestMethod] diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs index 0a598033..f08acf9e 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; @@ -13,6 +14,7 @@ public DemoController(ILogger logger) this._logger = logger; } + [AllowAnonymous] public async Task Get() { return this.Ok("好"); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index f6fb3d9d..2616f652 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -10,8 +10,8 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Logging.AddConsole(); -builder.Services.AddAuthentication("Basic") - .AddScheme("Basic", null); +builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) + .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, null); builder.Services.AddSingleton(); // builder.Services.AddSingleton(); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index b10e244c..43f09cdf 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Options; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; @@ -10,7 +11,6 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public class BasicAuthenticationHandler : AuthenticationHandler { private const string AuthorizationHeaderName = "Authorization"; - private const string BasicSchemeName = BasicAuthenticationDefaults.AuthenticationScheme; private readonly IBasicAuthenticationProvider _authenticationProvider; public BasicAuthenticationHandler( @@ -26,35 +26,39 @@ public BasicAuthenticationHandler( protected override async Task HandleAuthenticateAsync() { - if (!this.Request.Headers.ContainsKey(AuthorizationHeaderName)) + var schemeName = this.Scheme.Name; //由外部注入 + var endpoint = this.Context.GetEndpoint(); + if (endpoint?.Metadata?.GetMetadata() != null) { - // return AuthenticateResult.Fail("Invalid Basic authentication header"); - //Authorization header not in request return AuthenticateResult.NoResult(); } + if (!this.Request.Headers.ContainsKey(AuthorizationHeaderName)) + { + return AuthenticateResult.Fail("Invalid basic authentication header"); + } + if (!AuthenticationHeaderValue.TryParse(this.Request.Headers[AuthorizationHeaderName], - out var headerValue)) + out var authHeaderValue)) { - //Invalid Authorization header - return AuthenticateResult.NoResult(); + return AuthenticateResult.Fail("Invalid authorization Header"); } - if (headerValue.Scheme.StartsWith(BasicSchemeName, StringComparison.InvariantCultureIgnoreCase) == false) + if (authHeaderValue.Scheme.StartsWith(schemeName, StringComparison.InvariantCultureIgnoreCase) == false) { - return AuthenticateResult.NoResult(); + return AuthenticateResult.Fail("Invalid authorization scheme name"); } - var headerValueBytes = Convert.FromBase64String(headerValue.Parameter); - var userAndPassword = Encoding.UTF8.GetString(headerValueBytes); - var parts = userAndPassword.Split(':'); - if (parts.Length != 2) + var credentialBytes = Convert.FromBase64String(authHeaderValue.Parameter); + var userAndPassword = Encoding.UTF8.GetString(credentialBytes); + var credentials = userAndPassword.Split(':'); + if (credentials.Length != 2) { - return AuthenticateResult.Fail("Invalid Basic authentication header"); + return AuthenticateResult.Fail("Invalid basic authentication header"); } - var user = parts[0]; - var password = parts[1]; + var user = credentials[0]; + var password = credentials[1]; var isValidate = await this._authenticationProvider.IsValidateAsync(user, password, CancellationToken.None); @@ -74,10 +78,11 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop private AuthenticateResult SignIn(string user) { + var schemeName = this.Scheme.Name; var claims = new[] { new Claim(ClaimTypes.Name, user) }; - var identity = new ClaimsIdentity(claims, this.Scheme.Name); + var identity = new ClaimsIdentity(claims, schemeName); var principal = new ClaimsPrincipal(identity); - var ticket = new AuthenticationTicket(principal, this.Scheme.Name); + var ticket = new AuthenticationTicket(principal, schemeName); return AuthenticateResult.Success(ticket); } } \ No newline at end of file From 47245684d9dfa536d77adc90a7461716aa855c49 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 22:38:05 +0800 Subject: [PATCH 218/267] refactor --- .../UnitTest1.cs | 5 ++-- .../Program.cs | 6 +++-- .../BasicAuthenticationExtensions.cs | 25 +++++++++---------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs index fcd14617..ae869cf0 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -46,8 +47,8 @@ public void 訪問受保護的服務_驗證失敗() var clientSecret = "9527"; using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); var response = httpClient.SendAsync(requestMessage).Result; - response.Headers.TryGetValues("WWW-Authenticate", out var result); - Console.WriteLine($"驗證失敗:{result}"); + response.Headers.TryGetValues("WWW-Authenticate", out var values); + Console.WriteLine($"驗證失敗:{values.First()}"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); } diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index 2616f652..2743337e 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -10,11 +10,13 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Logging.AddConsole(); + builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) - .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, null); + .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, + p => new BasicAuthenticationOptions()); builder.Services.AddSingleton(); -// builder.Services.AddSingleton(); +// builder.Services.AddBasicAuthentication(); var app = builder.Build(); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs index d01902b6..bfb37867 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs @@ -5,25 +5,24 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public static class BasicAuthenticationExtensions { - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) + public static AuthenticationBuilder AddBasicAuthentication(this IServiceCollection services) where TAuthService : class, IBasicAuthenticationProvider - { - return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); - } + => services.AddAuthentication(o => o.DefaultScheme = BasicAuthenticationDefaults.AuthenticationScheme) + .AddBasic(); + + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) + where TAuthService : class, IBasicAuthenticationProvider => + AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme) - where TAuthService : class, IBasicAuthenticationProvider - { - return AddBasic(builder, authenticationScheme, _ => { }); - } + where TAuthService : class, IBasicAuthenticationProvider => + AddBasic(builder, authenticationScheme, _ => { }); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action configureOptions) - where TAuthService : class, IBasicAuthenticationProvider - { - return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); - } + where TAuthService : class, IBasicAuthenticationProvider => + AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) @@ -31,7 +30,7 @@ public static AuthenticationBuilder AddBasic(this AuthenticationBu { builder.Services .AddSingleton, BasicAuthenticationPostConfigureOptions>(); - builder.Services.AddTransient(); + builder.Services.AddSingleton(); return builder.AddScheme( authenticationScheme, configureOptions); From 15c3376487ee8827c1ec73886a6ca80172bdc270 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 23:08:06 +0800 Subject: [PATCH 219/267] refactor --- .../UnitTest1.cs | 1 - .../BasicAuthenticationExtensions.cs | 30 ++++++++++++------- .../BasicAuthenticationHandler.cs | 19 ++++++++---- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs index ae869cf0..4166dea8 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs @@ -21,7 +21,6 @@ public void 訪問不需要授權的服務() var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); - } [TestMethod] diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs index bfb37867..76845c24 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs @@ -5,24 +5,25 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authenticatio public static class BasicAuthenticationExtensions { - public static AuthenticationBuilder AddBasicAuthentication(this IServiceCollection services) - where TAuthService : class, IBasicAuthenticationProvider - => services.AddAuthentication(o => o.DefaultScheme = BasicAuthenticationDefaults.AuthenticationScheme) - .AddBasic(); - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) - where TAuthService : class, IBasicAuthenticationProvider => - AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); + } public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme) - where TAuthService : class, IBasicAuthenticationProvider => - AddBasic(builder, authenticationScheme, _ => { }); + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, authenticationScheme, _ => { }); + } public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action configureOptions) - where TAuthService : class, IBasicAuthenticationProvider => - AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); + where TAuthService : class, IBasicAuthenticationProvider + { + return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); + } public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) @@ -35,4 +36,11 @@ public static AuthenticationBuilder AddBasic(this AuthenticationBu return builder.AddScheme( authenticationScheme, configureOptions); } + + public static AuthenticationBuilder AddBasicAuthentication(this IServiceCollection services) + where TAuthService : class, IBasicAuthenticationProvider + { + return services.AddAuthentication(o => o.DefaultScheme = BasicAuthenticationDefaults.AuthenticationScheme) + .AddBasic(); + } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index 43f09cdf..bee997de 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -4,6 +4,7 @@ using System.Text.Encodings.Web; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Options; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; @@ -12,6 +13,7 @@ public class BasicAuthenticationHandler : AuthenticationHandler options, @@ -35,17 +37,20 @@ protected override async Task HandleAuthenticateAsync() if (!this.Request.Headers.ContainsKey(AuthorizationHeaderName)) { - return AuthenticateResult.Fail("Invalid basic authentication header"); + this._failReason = "Invalid basic authentication header"; + return AuthenticateResult.Fail(this._failReason); } if (!AuthenticationHeaderValue.TryParse(this.Request.Headers[AuthorizationHeaderName], out var authHeaderValue)) { - return AuthenticateResult.Fail("Invalid authorization Header"); + this._failReason = "Invalid authorization Header"; + return AuthenticateResult.Fail(this._failReason); } if (authHeaderValue.Scheme.StartsWith(schemeName, StringComparison.InvariantCultureIgnoreCase) == false) { + this._failReason = "Invalid authorization scheme name"; return AuthenticateResult.Fail("Invalid authorization scheme name"); } @@ -54,7 +59,8 @@ protected override async Task HandleAuthenticateAsync() var credentials = userAndPassword.Split(':'); if (credentials.Length != 2) { - return AuthenticateResult.Fail("Invalid basic authentication header"); + this._failReason = "Invalid basic authentication header"; + return AuthenticateResult.Fail(this._failReason); } var user = credentials[0]; @@ -64,7 +70,8 @@ protected override async Task HandleAuthenticateAsync() if (!isValidate) { - return AuthenticateResult.Fail("Invalid username or password"); + this._failReason = "Invalid username or password"; + return AuthenticateResult.Fail(this._failReason); } return this.SignIn(user); @@ -72,8 +79,10 @@ protected override async Task HandleAuthenticateAsync() protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { + this.Response.StatusCode = 401; + this.Response.HttpContext.Features.Get().ReasonPhrase = this._failReason; this.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{this.Options.Realm}\", charset=\"UTF-8\""; - await base.HandleChallengeAsync(properties); + await Task.CompletedTask; } private AuthenticateResult SignIn(string user) From 25a093e959acd1cb60b9775e8d0f54d2c90a32cb Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 22 May 2022 23:21:02 +0800 Subject: [PATCH 220/267] refactor --- .../Controllers/DemoController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs index f08acf9e..65298d94 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/DemoController.cs @@ -15,6 +15,7 @@ public DemoController(ILogger logger) } [AllowAnonymous] + [HttpGet] public async Task Get() { return this.Ok("好"); From 57534f33183b7062becb383210d8f5d75ac25554 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 16 Jun 2022 13:47:22 +0800 Subject: [PATCH 221/267] feat: add project --- .../Lab.AspNetCoreMiddleware.UnitTest.csproj | 23 ++++ .../ValidateRequireHeaderMiddlewareTests.cs | 106 +++++++++++++++++ .../ValidateRequireHeaderMiddlewareTests2.cs | 109 ++++++++++++++++++ .../Lab.AspNetCoreMiddleware.Web.csproj | 9 ++ .../Lab.AspNetCoreMiddleware.Web/Program.cs | 7 ++ .../Properties/launchSettings.json | 28 +++++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 ++ .../Lab.AspNetCoreMiddleware.sln | 28 +++++ .../Lab.AspNetCoreMiddleware/Failure.cs | 8 ++ .../Lab.AspNetCoreMiddleware/FailureCode.cs | 8 ++ .../Lab.AspNetCoreMiddleware/FailureResult.cs | 12 ++ .../Lab.AspNetCoreMiddleware/HeaderNames.cs | 7 ++ .../Lab.AspNetCoreMiddleware.csproj | 14 +++ .../ValidateRequiredHeaderMiddleware.cs | 71 ++++++++++++ 15 files changed, 447 insertions(+) create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests2.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Lab.AspNetCoreMiddleware.Web.csproj create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Program.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Properties/launchSettings.json create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.Development.json create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.json create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.sln create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Failure.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureCode.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureResult.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/HeaderNames.cs create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/ValidateRequiredHeaderMiddleware.cs diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj new file mode 100644 index 00000000..02fadc0b --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests.cs new file mode 100644 index 00000000..fd44b1bd --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.JsonDiffPatch.MsTest; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCoreMiddleware.UnitTest; + +[TestClass] +public class ValidateRequireHeaderMiddlewareTests +{ + [TestMethod] + public async Task HeaderCode型別錯誤會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_TYPE"", + ""propertyName"": ""X-Code"", + ""messages"": ""'abc' not numbers"", + ""value"": ""abc"" + } + ] +} +"; + using var testServer = await CreateTestServer(); + var httpContext = await testServer.SendAsync(context => + { + context.Request.Headers[HeaderNames.UserId] = "yao"; + context.Request.Headers[HeaderNames.Code] = "abc"; + }); + var response = httpContext.Response; + var stream = response.Body; + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + [TestMethod] + public async Task 所有Header為空會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-User-Id"", + ""messages"": ""The 'X-User-Id' header is required."" + }, + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-Code"", + ""messages"": ""The 'X-Code' header is required."" + } + ] +} +"; + using var testServer = await CreateTestServer(); + var httpContext = await testServer.SendAsync(context => { }); + var response = httpContext.Response; + var stream = response.Body; + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + private static async Task CreateTestServer() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(p => new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = false, + + // Encoder = JavaScriptEncoder.Create(UnicodeRanges.All, UnicodeRanges.All), + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }); + }) + .Configure(app => { app.UseMiddleware(); }); + }) + .StartAsync(); + + var server = host.GetTestServer(); + server.BaseAddress = new Uri("https://我真的是假的/不要打我的臉/"); + return server; + } +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests2.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests2.cs new file mode 100644 index 00000000..aa36cda6 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests2.cs @@ -0,0 +1,109 @@ +using System; +using System.IO; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.JsonDiffPatch.MsTest; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCoreMiddleware.UnitTest; + +[TestClass] +public class ValidateRequireHeaderMiddlewareTests2 +{ + [TestMethod] + public async Task HeaderCode型別錯誤會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_TYPE"", + ""propertyName"": ""X-Code"", + ""messages"": ""'abc' not numbers"", + ""value"": ""abc"" + } + ] +} +"; + var jsonSerializerOptions = CreateJsonSerializerOptions(); + var httpContext = new DefaultHttpContext() + { + Response = { Body = new MemoryStream()} + }; + httpContext.Request.Headers[HeaderNames.UserId] = "yao"; + httpContext.Request.Headers[HeaderNames.Code] = "abc"; + var target = new ValidateRequiredHeaderMiddleware((_) => Task.CompletedTask); + await target.InvokeAsync(httpContext, jsonSerializerOptions); + var response = httpContext.Response; + var stream = response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + [TestMethod] + public async Task 所有Header為空會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-User-Id"", + ""messages"": ""The 'X-User-Id' header is required."" + }, + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-Code"", + ""messages"": ""The 'X-Code' header is required."" + } + ] +} +"; + var jsonSerializerOptions = CreateJsonSerializerOptions(); + var httpContext = new DefaultHttpContext + { + Response = { Body = new MemoryStream() } + }; + + var target = new ValidateRequiredHeaderMiddleware(_ => Task.CompletedTask); + await target.InvokeAsync(httpContext, jsonSerializerOptions); + var response = httpContext.Response; + var stream = response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + private static JsonSerializerOptions CreateJsonSerializerOptions() + { + return new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = false, + + // Encoder = JavaScriptEncoder.Create(UnicodeRanges.All, UnicodeRanges.All), + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + } +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Lab.AspNetCoreMiddleware.Web.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Lab.AspNetCoreMiddleware.Web.csproj new file mode 100644 index 00000000..d52487bf --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Lab.AspNetCoreMiddleware.Web.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Program.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Program.cs new file mode 100644 index 00000000..d61665b8 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Program.cs @@ -0,0 +1,7 @@ +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.MapGet("/", () => "Hello World!"); + +app.Run(); +public partial class Program { } \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Properties/launchSettings.json b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Properties/launchSettings.json new file mode 100644 index 00000000..e1658380 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55168", + "sslPort": 44383 + } + }, + "profiles": { + "Lab.AspNetCoreMiddleware.Web": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7251;http://localhost:5251", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.Development.json b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.json b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.sln b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.sln new file mode 100644 index 00000000..e2a0a451 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCoreMiddleware", "Lab.AspNetCoreMiddleware\Lab.AspNetCoreMiddleware.csproj", "{FA33EE2C-5954-457C-9A00-ED3FDD38010C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCoreMiddleware.UnitTest", "Lab.AspNetCoreMiddleware.UnitTest\Lab.AspNetCoreMiddleware.UnitTest.csproj", "{FAB4FE0D-4232-46EA-A0A4-038D419E208F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCoreMiddleware.Web", "Lab.AspNetCoreMiddleware.Web\Lab.AspNetCoreMiddleware.Web.csproj", "{93EB2B91-FE4B-4A90-B517-8A959AD0BC40}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FA33EE2C-5954-457C-9A00-ED3FDD38010C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA33EE2C-5954-457C-9A00-ED3FDD38010C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA33EE2C-5954-457C-9A00-ED3FDD38010C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA33EE2C-5954-457C-9A00-ED3FDD38010C}.Release|Any CPU.Build.0 = Release|Any CPU + {FAB4FE0D-4232-46EA-A0A4-038D419E208F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAB4FE0D-4232-46EA-A0A4-038D419E208F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAB4FE0D-4232-46EA-A0A4-038D419E208F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAB4FE0D-4232-46EA-A0A4-038D419E208F}.Release|Any CPU.Build.0 = Release|Any CPU + {93EB2B91-FE4B-4A90-B517-8A959AD0BC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93EB2B91-FE4B-4A90-B517-8A959AD0BC40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93EB2B91-FE4B-4A90-B517-8A959AD0BC40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93EB2B91-FE4B-4A90-B517-8A959AD0BC40}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Failure.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Failure.cs new file mode 100644 index 00000000..24909aa8 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Failure.cs @@ -0,0 +1,8 @@ +namespace Lab.AspNetCoreMiddleware; + +internal class Failure +{ + public string Code { get; init; } + + public IEnumerable Messages { get; init; } +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureCode.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureCode.cs new file mode 100644 index 00000000..aa0463a7 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureCode.cs @@ -0,0 +1,8 @@ +namespace Lab.AspNetCoreMiddleware; + +enum FailureCode +{ + INVALID_FORMAT, + INVALID_REQUEST, + INVALID_TYPE +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureResult.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureResult.cs new file mode 100644 index 00000000..6c659c4d --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/FailureResult.cs @@ -0,0 +1,12 @@ +namespace Lab.AspNetCoreMiddleware; + +internal class FailureResult +{ + public string Code { get; init; } + + public string PropertyName { get; init; } + + public string Messages { get; init; } + + public string Value { get; init ; } +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/HeaderNames.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/HeaderNames.cs new file mode 100644 index 00000000..b3a8dd66 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/HeaderNames.cs @@ -0,0 +1,7 @@ +namespace Lab.AspNetCoreMiddleware; + +public class HeaderNames +{ + public static string UserId = "X-User-Id"; + public static string Code = "X-Code"; +} \ No newline at end of file diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj new file mode 100644 index 00000000..76bd7e85 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/ValidateRequiredHeaderMiddleware.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/ValidateRequiredHeaderMiddleware.cs new file mode 100644 index 00000000..7b8b594b --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/ValidateRequiredHeaderMiddleware.cs @@ -0,0 +1,71 @@ +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.Http; + +namespace Lab.AspNetCoreMiddleware; + +public class ValidateRequiredHeaderMiddleware +{ + private readonly string[] _requireHeaderNames = + { + HeaderNames.UserId, + HeaderNames.Code, + }; + + private readonly RequestDelegate _next; + + public ValidateRequiredHeaderMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, + JsonSerializerOptions jsonSerializerOptions) + { + var failureResults = new List(); + foreach (var name in this._requireHeaderNames) + { + if (context.Request.Headers.TryGetValue(name, out var value) == false) + { + failureResults.Add(new FailureResult + { + Code = FailureCode.INVALID_FORMAT.ToString(), + PropertyName = name, + Messages = $"The '{name}' header is required.", + }); + } + else + { + if (name == HeaderNames.Code) + { + if (long.TryParse(value, out var code) == false) + { + failureResults.Add(new FailureResult + { + Code = FailureCode.INVALID_TYPE.ToString(), + PropertyName = name, + Value = value, + Messages = $"'{value}' not numbers", + }); + } + } + } + } + + if (failureResults.Count > 0) + { + var failure = new Failure + { + Code = FailureCode.INVALID_REQUEST.ToString(), + Messages = failureResults + }; + var failureJson = JsonSerializer.Serialize(failure, jsonSerializerOptions); + context.Response.StatusCode = 400; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(failureJson, Encoding.UTF8, context.RequestAborted); + return; + } + + await this._next(context); + } +} \ No newline at end of file From aee7db6b03216db659c4e76d23a1032074ce4ea1 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 16 Jun 2022 15:33:55 +0800 Subject: [PATCH 222/267] refactor --- .../Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj index 76bd7e85..2a1c792b 100644 --- a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.csproj @@ -7,7 +7,7 @@ - + From ad7760d49ac7456cf908784291ae27d255c17ec2 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 16 Jun 2022 16:14:06 +0800 Subject: [PATCH 223/267] add test client test case --- .../Lab.AspNetCoreMiddleware.UnitTest.csproj | 14 +-- .../ValidateRequireHeaderMiddlewareTests1.cs | 98 +++++++++++++++++++ 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests1.cs diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj index 02fadc0b..3b992dbd 100644 --- a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj @@ -8,16 +8,16 @@ - - - - - - + + + + + + - + diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests1.cs b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests1.cs new file mode 100644 index 00000000..61530a38 --- /dev/null +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/ValidateRequireHeaderMiddlewareTests1.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.JsonDiffPatch.MsTest; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCoreMiddleware.UnitTest; + +[TestClass] +public class ValidateRequireHeaderMiddlewareTests1 +{ + [TestMethod] + public async Task HeaderCode型別錯誤會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_TYPE"", + ""propertyName"": ""X-Code"", + ""messages"": ""'abc' not numbers"", + ""value"": ""abc"" + } + ] +} +"; + using var httpClient = await CreateTestClient(); + var request = new HttpRequestMessage(HttpMethod.Get, "/青菜"); + request.Headers.Add(HeaderNames.UserId, "yao"); + request.Headers.Add(HeaderNames.Code, "abc"); + var response = await httpClient.SendAsync(request); + var actual = await response.Content.ReadAsStringAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + [TestMethod] + public async Task 所有Header為空會驗證失敗() + { + var expected = @" +{ + ""code"": ""INVALID_REQUEST"", + ""messages"": [ + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-User-Id"", + ""messages"": ""The 'X-User-Id' header is required."" + }, + { + ""code"": ""INVALID_FORMAT"", + ""propertyName"": ""X-Code"", + ""messages"": ""The 'X-Code' header is required."" + } + ] +} +"; + using var httpClient = await CreateTestClient(); + var request = new HttpRequestMessage(HttpMethod.Get, "/青菜"); + var response = await httpClient.SendAsync(request); + var actual = await response.Content.ReadAsStringAsync(); + Assert.That.JsonAreEqual(expected, actual, true); + } + + private static async Task CreateTestClient() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(p => new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = false, + + // Encoder = JavaScriptEncoder.Create(UnicodeRanges.All, UnicodeRanges.All), + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }); + }) + .Configure(app => { app.UseMiddleware(); }); + }) + .StartAsync(); + return host.GetTestClient(); + } +} \ No newline at end of file From 179f1a5a95ae11cb4125ffba88f745a710700d8d Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 17 Jun 2022 19:19:01 +0800 Subject: [PATCH 224/267] refactor --- .../Lab.AspNetCoreMiddleware.UnitTest.csproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj index 3b992dbd..37e06a28 100644 --- a/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj +++ b/Test/Lab.AspNetCoreMiddleware/Lab.AspNetCoreMiddleware.UnitTest/Lab.AspNetCoreMiddleware.UnitTest.csproj @@ -8,16 +8,16 @@ - - - - - - + + + + + + - + From 1edfd775e28110790bfbf1d8fb4d069478b54624 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 18 Jun 2022 23:02:04 +0800 Subject: [PATCH 225/267] feat: add unit test --- ...64\345\220\210\346\270\254\350\251\246.cs" | 2 +- .../Controllers/TestController.cs | 25 ++++ ...56\345\205\203\346\270\254\350\251\246.cs" | 116 ++++++++++++++++++ ...ty.BasicAuthenticationSite.UnitTest.csproj | 22 ++++ .../Program.cs | 10 +- .../BasicAuthenticationExtensions.cs | 43 ++----- .../BasicAuthenticationHandler.cs | 1 + .../DefaultBasicAuthenticationProvider.cs | 9 ++ .../Lab.AspNetCore.Security.sln | 6 + 9 files changed, 196 insertions(+), 38 deletions(-) rename WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs => "WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" (97%) create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs create mode 100644 "WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest.csproj create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/DefaultBasicAuthenticationProvider.cs diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" similarity index 97% rename from WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs rename to "WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" index 4166dea8..be40b7a4 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/UnitTest1.cs +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" @@ -9,7 +9,7 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; [TestClass] -public class UnitTest1 +public class BasicAuthenticationMiddleware整合測試 { [TestMethod] public void 訪問不需要授權的服務() diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs new file mode 100644 index 00000000..bbbdca23 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers; + +[ApiController] +[Route("[controller]")] +public class TestController : ControllerBase +{ + private readonly ILogger _logger; + + public TestController(ILogger logger) + { + this._logger = logger; + } + + [AllowAnonymous] + [HttpGet] + public async Task Get() + { + return this.Ok("好"); + } +} \ No newline at end of file diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" new file mode 100644 index 00000000..a6d428b4 --- /dev/null +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -0,0 +1,116 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest; + +[TestClass] +public class BasicAuthenticationMiddleware單元測試 +{ + [TestMethod] + public async Task aaaa() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(); + services.AddBasicAuthentication(_ => { }); + services.AddAuthorization(); + }) + .Configure(app => + { + app.UseAuthentication(); + app.UseAuthorization(); + }); + }) + .StartAsync(); + var optionsMonitor = host.Services.GetService>(); + var handler = host.Services.GetService(); + var authenticationScheme = new AuthenticationScheme("basic", "basic", typeof(BasicAuthenticationHandler)); + handler.InitializeAsync(authenticationScheme, new DefaultHttpContext()); + var authenticateResult = await handler.AuthenticateAsync(); + + using var server = await CreateTestServer(); + var httpContext = await server.SendAsync(config => + { + config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527xxxx"); + }); + + // 驗證失敗沒有觸發 BasicAuthenticationHandler.HandleChallengeAsync + var userPrincipal = httpContext.User; + Assert.AreEqual(false, userPrincipal.Identity.IsAuthenticated); + } + + [TestMethod] + public async Task 驗證失敗() + { + using var server = await CreateTestServer(); + var httpContext = await server.SendAsync(config => + { + config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527xxxx"); + }); + + // 驗證失敗沒有觸發 BasicAuthenticationHandler.HandleChallengeAsync + var userPrincipal = httpContext.User; + Assert.AreEqual(false, userPrincipal.Identity.IsAuthenticated); + } + + [TestMethod] + public async Task 驗證成功() + { + using var server = await CreateTestServer(); + var httpContext = await server.SendAsync(config => + { + config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527"); + }); + var userPrincipal = httpContext.User; + Assert.AreEqual(true, userPrincipal.Identity.IsAuthenticated); + } + + private static string CreateBasicAuthenticationValue(string userId, string password) + { + var certificate = $"{userId}:{password}"; + var base64Encode = Convert.ToBase64String(Encoding.ASCII.GetBytes(certificate)); + return $"Basic {base64Encode}"; + } + + private static async Task CreateTestServer() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(); + services.AddBasicAuthentication(_ => { }); + services.AddAuthorization(); + }) + .Configure(app => + { + app.UseAuthentication(); + app.UseAuthorization(); + }); + }) + .StartAsync(); + + var server = host.GetTestServer(); + server.BaseAddress = new Uri("https://我真的是假的/不要打我的臉/"); + return server; + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest.csproj new file mode 100644 index 00000000..65a60277 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index 2743337e..c86fbb6f 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -11,12 +11,12 @@ builder.Services.AddSwaggerGen(); builder.Logging.AddConsole(); -builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) - .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, - p => new BasicAuthenticationOptions()); -builder.Services.AddSingleton(); +// builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) +// .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, +// p => new BasicAuthenticationOptions()); +// builder.Services.AddSingleton(); -// builder.Services.AddBasicAuthentication(); +builder.Services.AddBasicAuthentication(options => { }); var app = builder.Build(); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs index 76845c24..267ade4f 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationExtensions.cs @@ -1,46 +1,25 @@ using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Options; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; public static class BasicAuthenticationExtensions { - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) - where TAuthService : class, IBasicAuthenticationProvider - { - return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, _ => { }); - } - - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, - string authenticationScheme) - where TAuthService : class, IBasicAuthenticationProvider - { - return AddBasic(builder, authenticationScheme, _ => { }); - } - - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, + public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action configureOptions) - where TAuthService : class, IBasicAuthenticationProvider { - return AddBasic(builder, BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); - } - - public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, - string authenticationScheme, Action configureOptions) - where TAuthService : class, IBasicAuthenticationProvider - { - builder.Services - .AddSingleton, BasicAuthenticationPostConfigureOptions>(); - builder.Services.AddSingleton(); - return builder.AddScheme( - authenticationScheme, configureOptions); + BasicAuthenticationDefaults.AuthenticationScheme, + BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); } - public static AuthenticationBuilder AddBasicAuthentication(this IServiceCollection services) - where TAuthService : class, IBasicAuthenticationProvider + public static AuthenticationBuilder AddBasicAuthentication(this IServiceCollection services, + Action configureOptions) { - return services.AddAuthentication(o => o.DefaultScheme = BasicAuthenticationDefaults.AuthenticationScheme) - .AddBasic(); + return services.AddAuthentication(o => + { + o.DefaultAuthenticateScheme = BasicAuthenticationDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = BasicAuthenticationDefaults.AuthenticationScheme; + }) + .AddBasic(configureOptions); } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index bee997de..789d7ca5 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -79,6 +79,7 @@ protected override async Task HandleAuthenticateAsync() protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { + // todo: write detail log this.Response.StatusCode = 401; this.Response.HttpContext.Features.Get().ReasonPhrase = this._failReason; this.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{this.Options.Realm}\", charset=\"UTF-8\""; diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/DefaultBasicAuthenticationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/DefaultBasicAuthenticationProvider.cs new file mode 100644 index 00000000..b90712b5 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/DefaultBasicAuthenticationProvider.cs @@ -0,0 +1,9 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; + +public class DefaultBasicAuthenticationProvider : IBasicAuthenticationProvider +{ + public Task IsValidateAsync(string user, string password, CancellationToken cancel = default) + { + return Task.FromResult(true); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln index 1d518087..1fc06b88 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCore.Security.Bas EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest", "Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest\Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj", "{13085C3E-F174-45D2-B8F7-3EE51D42DDF4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest", "Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest\Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest.csproj", "{12F00FC6-1D31-48CD-AB17-B00F76846A33}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {13085C3E-F174-45D2-B8F7-3EE51D42DDF4}.Release|Any CPU.Build.0 = Release|Any CPU + {12F00FC6-1D31-48CD-AB17-B00F76846A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12F00FC6-1D31-48CD-AB17-B00F76846A33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12F00FC6-1D31-48CD-AB17-B00F76846A33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12F00FC6-1D31-48CD-AB17-B00F76846A33}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From f0909525365ee61603ecddac91af091c3a32a99b Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 19 Jun 2022 14:41:45 +0800 Subject: [PATCH 226/267] refactor --- ...64\345\220\210\346\270\254\350\251\246.cs" | 4 +- .../Controllers/Models/User.cs | 8 ++++ .../Controllers/TestController.cs | 8 ++++ ...sicAuthenticationSite.IntegrateTest.csproj | 12 +++--- .../TestServer.cs | 5 ++- ...56\345\205\203\346\270\254\350\251\246.cs" | 39 ------------------- .../Controllers/User.cs | 8 ---- .../Controllers/UserController.cs | 27 ------------- ...re.Security.BasicAuthenticationSite.csproj | 4 +- .../Program.cs | 2 +- 10 files changed, 31 insertions(+), 86 deletions(-) create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/Models/User.cs delete mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs delete mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" index be40b7a4..5e71f2b0 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" @@ -16,7 +16,7 @@ public void 訪問不需要授權的服務() { var server = new TestServer(); var httpClient = server.CreateClient(); - var url = "demo"; + var url = "test"; var response = httpClient.GetAsync(url).Result; var result = response.Content.ReadAsStringAsync().Result; Console.WriteLine(result); @@ -28,7 +28,7 @@ public void 訪問受保護的服務() { var server = new TestServer(); var httpClient = server.CreateClient(); - var url = "user"; + var url = "test"; var clientId = "YAO"; var clientSecret = "9527"; using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/Models/User.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/Models/User.cs new file mode 100644 index 00000000..d923c451 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/Models/User.cs @@ -0,0 +1,8 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers.Models; + +public class User +{ + public string Name { get; set; } + + public int Age { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs index bbbdca23..c453bc1a 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -22,4 +23,11 @@ public async Task Get() { return this.Ok("好"); } + + [Authorize] + [HttpPost] + public async Task Post(User user) + { + return this.Ok("好"); + } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj index 90db6373..a9c20e84 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj @@ -8,14 +8,14 @@ - - - - - + + + + + - + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs index 3ddc0f95..b94e4863 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/TestServer.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Hosting; +using Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; @@ -8,6 +9,8 @@ public class TestServer : WebApplicationFactory { private void ConfigureServices(IServiceCollection services) { + services.AddControllers() + .AddApplicationPart(typeof(TestController).Assembly); } protected override void ConfigureWebHost(IWebHostBuilder builder) diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" index a6d428b4..07915dc4 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest; @@ -17,44 +16,6 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest; [TestClass] public class BasicAuthenticationMiddleware單元測試 { - [TestMethod] - public async Task aaaa() - { - var host = await new HostBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseTestServer() - .ConfigureServices( - services => - { - services.AddSingleton(); - services.AddBasicAuthentication(_ => { }); - services.AddAuthorization(); - }) - .Configure(app => - { - app.UseAuthentication(); - app.UseAuthorization(); - }); - }) - .StartAsync(); - var optionsMonitor = host.Services.GetService>(); - var handler = host.Services.GetService(); - var authenticationScheme = new AuthenticationScheme("basic", "basic", typeof(BasicAuthenticationHandler)); - handler.InitializeAsync(authenticationScheme, new DefaultHttpContext()); - var authenticateResult = await handler.AuthenticateAsync(); - - using var server = await CreateTestServer(); - var httpContext = await server.SendAsync(config => - { - config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527xxxx"); - }); - - // 驗證失敗沒有觸發 BasicAuthenticationHandler.HandleChallengeAsync - var userPrincipal = httpContext.User; - Assert.AreEqual(false, userPrincipal.Identity.IsAuthenticated); - } - [TestMethod] public async Task 驗證失敗() { diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs deleted file mode 100644 index acde4a03..00000000 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/User.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; - -internal class User -{ - public string Name { get; set; } - - public int Age { get; set; } -} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs deleted file mode 100644 index 695aa74a..00000000 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Controllers/UserController.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Controllers; - -[ApiController] -[Route("[controller]")] -public class UserController : ControllerBase -{ - private readonly ILogger _logger; - - public UserController(ILogger logger) - { - this._logger = logger; - } - - [HttpGet] - [Authorize] - public async Task Get() - { - return this.Ok(new User - { - Name = this.User.Identity.Name, - Age = 18 - }); - } -} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj index b4a1e621..ab416cfa 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index c86fbb6f..d1f5376b 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -14,7 +14,7 @@ // builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) // .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, // p => new BasicAuthenticationOptions()); -// builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddBasicAuthentication(options => { }); From 95c4cbc385a61555a8d60f816f2e715927f2fb20 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 19 Jun 2022 16:18:38 +0800 Subject: [PATCH 227/267] =?UTF-8?q?add=20BasicAuthenticationHandler?= =?UTF-8?q?=E5=96=AE=E5=85=83=E6=B8=AC=E8=A9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...sicAuthenticationSite.IntegrateTest.csproj | 12 +- ...56\345\205\203\346\270\254\350\251\246.cs" | 167 ++++++++++++++++++ ...56\345\205\203\346\270\254\350\251\246.cs" | 2 - ...re.Security.BasicAuthenticationSite.csproj | 4 +- .../BasicAuthenticationHandler.cs | 21 ++- 5 files changed, 192 insertions(+), 14 deletions(-) create mode 100644 "WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj index a9c20e84..90db6373 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj @@ -8,14 +8,14 @@ - - - - - + + + + + - + diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" new file mode 100644 index 00000000..eb6e8b6e --- /dev/null +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -0,0 +1,167 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest; + +[TestClass] +public class BasicAuthenticationHandler單元測試 +{ + [TestMethod] + public async Task AuthenticateAsyncTest() + { + var context = new DefaultHttpContext(); + var authorizationHeader = new StringValues(string.Empty); + context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader); + + using var testHost = await CreateTestHost(); + var handler = testHost.Services.GetService(); + await handler.InitializeAsync(new AuthenticationScheme("basic", + "basic", + typeof(BasicAuthenticationHandler)), + context); + var result = await handler.AuthenticateAsync(); + + Assert.IsFalse(result.Succeeded); + Assert.AreEqual("Invalid authorization Header", result.Failure.Message); + } + + [TestMethod] + public async Task ChallengeAsyncTest() + { + var context = new DefaultHttpContext + { + Response = { Body = new MemoryStream() } + }; + + var authorizationHeader = new StringValues(string.Empty); + context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader); + + using var testHost = await CreateTestHost(); + var handler = testHost.Services.GetService(); + await handler.InitializeAsync(new AuthenticationScheme("basic", + "basic", + typeof(BasicAuthenticationHandler)), + context); + var authenticateResult = await handler.AuthenticateAsync(); + await handler.ChallengeAsync(authenticateResult.Properties); + var response = context.Response; + + // Assert.IsFalse(result.Succeeded); + var expected = "Basic realm=\"Demo Site\", charset=\"UTF-8\""; + Assert.AreEqual(expected, response.Headers.WWWAuthenticate.ToString()); + } + + [TestMethod] + public async Task 驗證成功() + { + using var server = await CreateTestServer(); + var httpContext = await server.SendAsync(config => + { + config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527"); + }); + var userPrincipal = httpContext.User; + Assert.AreEqual(true, userPrincipal.Identity.IsAuthenticated); + } + + private static AuthenticationHeaderValue CreateAuthenticationHeaderValue(string userId, string password) + { + var certificate = CreateBasicAuthenticationValue(userId, password); + var authenticationHeaderValue = new AuthenticationHeaderValue("basic", certificate); + return authenticationHeaderValue; + } + + private static string CreateBasicAuthenticationValue(string userId, string password) + { + var certificate = $"{userId}:{password}"; + var base64Encode = Convert.ToBase64String(Encoding.ASCII.GetBytes(certificate)); + return $"Basic {base64Encode}"; + } + + private static async Task CreateTestClient() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(); + services.AddBasicAuthentication(_ => { }); + services.AddAuthorization(); + }) + .Configure(app => + { + app.UseAuthentication(); + app.UseAuthorization(); + }); + }) + .StartAsync(); + + return host.GetTestClient(); + } + + private static async Task CreateTestHost() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(); + services.AddBasicAuthentication(_ => { }); + services.AddAuthorization(); + }) + .Configure(app => + { + app.UseAuthentication(); + app.UseAuthorization(); + }); + }) + .StartAsync(); + return host; + } + + private static async Task CreateTestServer() + { + var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseTestServer() + .ConfigureServices( + services => + { + services.AddSingleton(); + services.AddBasicAuthentication(_ => { }); + services.AddAuthorization(); + }) + .Configure(app => + { + app.UseAuthentication(); + app.UseAuthorization(); + }); + }) + .StartAsync(); + + var server = host.GetTestServer(); + server.BaseAddress = new Uri("https://我真的是假的/不要打我的臉/"); + return server; + } +} \ No newline at end of file diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" index 07915dc4..91941d36 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationMiddleware\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -2,10 +2,8 @@ using System.Text; using System.Threading.Tasks; using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj index ab416cfa..b4a1e621 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index 789d7ca5..5f85a0e1 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -6,12 +6,12 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; public class BasicAuthenticationHandler : AuthenticationHandler { - private const string AuthorizationHeaderName = "Authorization"; private readonly IBasicAuthenticationProvider _authenticationProvider; private string _failReason; @@ -35,13 +35,13 @@ protected override async Task HandleAuthenticateAsync() return AuthenticateResult.NoResult(); } - if (!this.Request.Headers.ContainsKey(AuthorizationHeaderName)) + if (!this.Request.Headers.ContainsKey(HeaderNames.Authorization)) { this._failReason = "Invalid basic authentication header"; return AuthenticateResult.Fail(this._failReason); } - if (!AuthenticationHeaderValue.TryParse(this.Request.Headers[AuthorizationHeaderName], + if (!AuthenticationHeaderValue.TryParse(this.Request.Headers[HeaderNames.Authorization], out var authHeaderValue)) { this._failReason = "Invalid authorization Header"; @@ -79,10 +79,23 @@ protected override async Task HandleAuthenticateAsync() protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { - // todo: write detail log + // 寫入詳細的失敗原因,排除敏感性資料 + this.Logger.LogInformation("{FailureReason}", new + { + Code = "InvalidAuthentication", + Message = this._failReason + }); + this.Response.StatusCode = 401; this.Response.HttpContext.Features.Get().ReasonPhrase = this._failReason; this.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{this.Options.Realm}\", charset=\"UTF-8\""; + + // 響應粗糙的內容,這不是標準的 Basic Authentication 失敗的回傳,僅是為了示意 + this.Response.WriteAsJsonAsync(new + { + Code = "InvalidAuthentication", + Message = "Please contact your administrator" + }); await Task.CompletedTask; } From 54673a6cab8f4216e8df728864d858fe37e21d86 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 19 Jun 2022 16:45:09 +0800 Subject: [PATCH 228/267] refactor --- ...56\345\205\203\346\270\254\350\251\246.cs" | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" index eb6e8b6e..733c1c64 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -22,7 +22,25 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest; public class BasicAuthenticationHandler單元測試 { [TestMethod] - public async Task AuthenticateAsyncTest() + public async Task 驗證成功() + { + var context = new DefaultHttpContext(); + var authorizationHeader = new StringValues(CreateBasicAuthenticationValue("yao", "9527")); + context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader); + + using var testHost = await CreateTestHost(); + var handler = testHost.Services.GetService(); + await handler.InitializeAsync(new AuthenticationScheme("basic", + "basic", + typeof(BasicAuthenticationHandler)), + context); + var result = await handler.AuthenticateAsync(); + + Assert.IsTrue(result.Succeeded); + } + + [TestMethod] + public async Task 驗證失敗() { var context = new DefaultHttpContext(); var authorizationHeader = new StringValues(string.Empty); @@ -41,14 +59,14 @@ await handler.InitializeAsync(new AuthenticationScheme("basic", } [TestMethod] - public async Task ChallengeAsyncTest() + public async Task 驗證失敗後回應錯誤() { var context = new DefaultHttpContext { Response = { Body = new MemoryStream() } }; - var authorizationHeader = new StringValues(string.Empty); + var authorizationHeader = new StringValues(CreateBasicAuthenticationValue("yao", "9527")); context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader); using var testHost = await CreateTestHost(); @@ -61,30 +79,11 @@ await handler.InitializeAsync(new AuthenticationScheme("basic", await handler.ChallengeAsync(authenticateResult.Properties); var response = context.Response; - // Assert.IsFalse(result.Succeeded); + Assert.IsFalse(authenticateResult.Succeeded); var expected = "Basic realm=\"Demo Site\", charset=\"UTF-8\""; Assert.AreEqual(expected, response.Headers.WWWAuthenticate.ToString()); } - [TestMethod] - public async Task 驗證成功() - { - using var server = await CreateTestServer(); - var httpContext = await server.SendAsync(config => - { - config.Request.Headers.Authorization = CreateBasicAuthenticationValue("yao", "9527"); - }); - var userPrincipal = httpContext.User; - Assert.AreEqual(true, userPrincipal.Identity.IsAuthenticated); - } - - private static AuthenticationHeaderValue CreateAuthenticationHeaderValue(string userId, string password) - { - var certificate = CreateBasicAuthenticationValue(userId, password); - var authenticationHeaderValue = new AuthenticationHeaderValue("basic", certificate); - return authenticationHeaderValue; - } - private static string CreateBasicAuthenticationValue(string userId, string password) { var certificate = $"{userId}:{password}"; From 512503be66192d25edc1c4da1099872b3526fdcf Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 20 Jun 2022 21:40:03 +0800 Subject: [PATCH 229/267] fix --- ...64\345\220\210\346\270\254\350\251\246.cs" | 26 ++++++++++++++----- .../Controllers/TestController.cs | 2 +- ...56\345\205\203\346\270\254\350\251\246.cs" | 2 +- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" index 5e71f2b0..892e61b5 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/BasicAuthenticationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" @@ -28,11 +28,15 @@ public void 訪問受保護的服務() { var server = new TestServer(); var httpClient = server.CreateClient(); - var url = "test"; + var url = "protect"; var clientId = "YAO"; var clientSecret = "9527"; - using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); - var response = httpClient.SendAsync(requestMessage).Result; + var request = new HttpRequestMessage(HttpMethod.Get, url) + { + Headers = { Authorization = CreateAuthenticationHeaderValue(clientId, clientSecret) } + }; + + var response = httpClient.SendAsync(request).Result; Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); } @@ -41,11 +45,14 @@ public void 訪問受保護的服務_驗證失敗() { var server = new TestServer(); var httpClient = server.CreateClient(); - var url = "user"; + var url = "protect"; var clientId = "YAO1234"; var clientSecret = "9527"; - using var requestMessage = CreateBasicAuthenticationRequest(url, clientId, clientSecret); - var response = httpClient.SendAsync(requestMessage).Result; + var request = new HttpRequestMessage(HttpMethod.Get, url) + { + Headers = { Authorization = CreateAuthenticationHeaderValue(clientId, clientSecret) } + }; + var response = httpClient.SendAsync(request).Result; response.Headers.TryGetValues("WWW-Authenticate", out var values); Console.WriteLine($"驗證失敗:{values.First()}"); Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); @@ -59,4 +66,11 @@ private static HttpRequestMessage CreateBasicAuthenticationRequest(string url, s requestMessage.Headers.Authorization = new AuthenticationHeaderValue("basic", base64Encoded); return requestMessage; } + + private static AuthenticationHeaderValue CreateAuthenticationHeaderValue(string clientId, string clientSecret) + { + var authenticationString = $"{clientId}:{clientSecret}"; + var base64Encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes(authenticationString)); + return new AuthenticationHeaderValue("basic", base64Encoded); + } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs index c453bc1a..7d3a574d 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/TestController.cs @@ -24,7 +24,7 @@ public async Task Get() return this.Ok("好"); } - [Authorize] + [AllowAnonymous] [HttpPost] public async Task Post(User user) { diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" index 733c1c64..aa95c4f2 100644 --- "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.UnitTest/BasicAuthenticationHandler\345\226\256\345\205\203\346\270\254\350\251\246.cs" @@ -66,7 +66,7 @@ public async Task 驗證失敗後回應錯誤() Response = { Body = new MemoryStream() } }; - var authorizationHeader = new StringValues(CreateBasicAuthenticationValue("yao", "9527")); + var authorizationHeader = new StringValues(CreateBasicAuthenticationValue("yao123", "9527")); context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader); using var testHost = await CreateTestHost(); From 80b3c9ae694a6f6593f292885676f38738543726 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 20 Jun 2022 21:56:37 +0800 Subject: [PATCH 230/267] fix --- .../Controllers/ProtectController.cs | 33 +++++++++++++++++++ ...sicAuthenticationSite.IntegrateTest.csproj | 12 +++---- ...re.Security.BasicAuthenticationSite.csproj | 7 ++-- .../PermissionAuthorizationHandler.cs | 27 +++++++++++++++ ...ionAuthorizationMiddlewareResultHandler.cs | 15 +++++++++ .../PermissionAuthorizationRequirement.cs | 7 ++++ 6 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/ProtectController.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/ProtectController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/ProtectController.cs new file mode 100644 index 00000000..b61a4108 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/ProtectController.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers; + +[ApiController] +[Route("[controller]")] +public class ProtectController : ControllerBase +{ + private readonly ILogger _logger; + + public ProtectController(ILogger logger) + { + this._logger = logger; + } + + [Authorize] + [HttpGet] + public async Task Get() + { + return this.Ok("好"); + } + + [AllowAnonymous] + [HttpPost] + public async Task Post(User user) + { + return this.Ok("好"); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj index 90db6373..a9c20e84 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.csproj @@ -8,14 +8,14 @@ - - - - - + + + + + - + diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj index b4a1e621..e71b3ef4 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Lab.AspNetCore.Security.BasicAuthenticationSite.csproj @@ -7,13 +7,10 @@ - - + + - - - diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs new file mode 100644 index 00000000..ffb736a0 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public class PermissionAuthorizationHandler : AuthorizationHandler +{ + + public override Task HandleAsync(AuthorizationHandlerContext context) + { + return base.HandleAsync(context); + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, + PermissionAuthorizationRequirement requirement) + { + if (context.User.Identity.IsAuthenticated==false) + { + + // return Task.FromResult(false); + } + + + + // Task.FromResult(false); + } + +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs new file mode 100644 index 00000000..578239d0 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Policy; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public class PermissionAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler +{ + public async Task HandleAsync(RequestDelegate next, + HttpContext context, + AuthorizationPolicy policy, + PolicyAuthorizationResult authorizeResult) + { + await next(context); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs new file mode 100644 index 00000000..96f2e195 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs @@ -0,0 +1,7 @@ +using Microsoft.AspNetCore.Authorization; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public class PermissionAuthorizationRequirement : IAuthorizationRequirement +{ +} \ No newline at end of file From 24323d8de6286aab2308aa2481a2032c2c62e7a7 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 21 Jun 2022 19:30:36 +0800 Subject: [PATCH 231/267] =?UTF-8?q?feat:=20=E5=AF=A6=E4=BD=9C=E6=8E=88?= =?UTF-8?q?=E6=AC=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PermissionController.cs | 34 +++++++++ ...64\345\220\210\346\270\254\350\251\246.cs" | 76 +++++++++++++++++++ .../FieldTypeAssistant.cs | 41 ++++++++++ .../Program.cs | 24 +++++- .../IPermissionAuthorizationProvider.cs | 6 ++ .../Security/Authorization/Permission.cs | 22 ++++++ .../PermissionAuthorizationHandler.cs | 25 ++++-- ...ionAuthorizationMiddlewareResultHandler.cs | 45 ++++++++++- .../PermissionAuthorizationPolicyProvider.cs | 53 +++++++++++++ .../PermissionAuthorizationProvider.cs | 21 +++++ .../PermissionAuthorizationRequirement.cs | 1 + 11 files changed, 336 insertions(+), 12 deletions(-) create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/PermissionController.cs create mode 100644 "WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/PermissionAuthorizationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/FieldTypeAssistant.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/IPermissionAuthorizationProvider.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs create mode 100644 WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationProvider.cs diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/PermissionController.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/PermissionController.cs new file mode 100644 index 00000000..d45d4f3b --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/Controllers/PermissionController.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers.Models; +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers; + +[ApiController] +[Route("[controller]")] +public class PermissionController : ControllerBase +{ + private readonly ILogger _logger; + + public PermissionController(ILogger logger) + { + this._logger = logger; + } + + [Authorize(Policy = Permission.Operation.Read)] + [HttpGet] + public async Task Get() + { + return this.Ok("好"); + } + + [AllowAnonymous] + [HttpPost] + public async Task Post(User user) + { + return this.Ok("好"); + } +} \ No newline at end of file diff --git "a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/PermissionAuthorizationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/PermissionAuthorizationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" new file mode 100644 index 00000000..2ada55db --- /dev/null +++ "b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest/PermissionAuthorizationMiddleware\346\225\264\345\220\210\346\270\254\350\251\246.cs" @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest.Controllers; +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.IntegrateTest; + +[TestClass] +public class PermissionAuthorizationMiddleware整合測試 +{ + [TestMethod] + public async Task 訪問受保護的服務_授權成功() + { + var server = CreateTestServer(); + var httpClient = server.CreateClient(); + var url = "permission"; + var clientId = "YAO"; + var clientSecret = "9527"; + var request = new HttpRequestMessage(HttpMethod.Get, url) + { + Headers = { Authorization = CreateAuthenticationHeaderValue(clientId, clientSecret) } + }; + var response = httpClient.SendAsync(request).Result; + var content = await response.Content.ReadAsStringAsync(); + Console.WriteLine(content); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + + [TestMethod] + public async Task 訪問受保護的服務_授權失敗() + { + var server = CreateTestServer(); + var httpClient = server.CreateClient(); + var url = "permission"; + var clientId = "jojo"; + var clientSecret = "9527"; + var request = new HttpRequestMessage(HttpMethod.Get, url) + { + Headers = { Authorization = CreateAuthenticationHeaderValue(clientId, clientSecret) } + }; + var response = httpClient.SendAsync(request).Result; + var content = await response.Content.ReadAsStringAsync(); + Console.WriteLine(content); + Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); + } + + private static WebApplicationFactory CreateTestServer() + { + var server = new WebApplicationFactory().WithWebHostBuilder(builder => + { + builder.ConfigureServices(services => + { + services.AddSingleton(); + services.AddControllers() + .AddApplicationPart(typeof(TestController).Assembly); + }); + }); + return server; + } + + private static AuthenticationHeaderValue CreateAuthenticationHeaderValue(string clientId, string clientSecret) + { + var authenticationString = $"{clientId}:{clientSecret}"; + var base64Encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes(authenticationString)); + return new AuthenticationHeaderValue("basic", base64Encoded); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/FieldTypeAssistant.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/FieldTypeAssistant.cs new file mode 100644 index 00000000..a40d46a2 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/FieldTypeAssistant.cs @@ -0,0 +1,41 @@ +using System.Collections.Concurrent; +using System.Reflection; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite; + +public class FieldTypeAssistant +{ + private static ConcurrentDictionary> s_fieldTypeList = new(); + + public static Dictionary GetEnumValues() + { + return Enum.GetValues(typeof(T)) + .Cast() + .ToDictionary(p => p.ToString(), p => p); + } + + public static Dictionary GetStaticFieldName() + { + var type = typeof(T); + var fieldTypeList = s_fieldTypeList; + if (fieldTypeList.TryGetValue(type, out var results)) + { + return results; + } + + var bindingFlags = BindingFlags.Public + | BindingFlags.Static + ; + results = new Dictionary(); + var fieldInfosInfos = type.GetFields(bindingFlags); + foreach (var fieldInfo in fieldInfosInfos) + { + var value = fieldInfo.GetValue(null); + + results.Add(value.ToString(), fieldInfo.FieldType); + } + + fieldTypeList.TryAdd(type, results); + return results; + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs index d1f5376b..26a528f4 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Program.cs @@ -1,4 +1,10 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Unicode; using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authentication; +using Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; +using Microsoft.AspNetCore.Authorization; var builder = WebApplication.CreateBuilder(args); @@ -14,9 +20,25 @@ // builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme) // .AddScheme(BasicAuthenticationDefaults.AuthenticationScheme, // p => new BasicAuthenticationOptions()); +builder.Services.AddSingleton(p=>new JsonSerializerOptions +{ + Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs), + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, +}); builder.Services.AddSingleton(); - builder.Services.AddBasicAuthentication(options => { }); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +// builder.Services.AddAuthorization(options => +// { +// options.AddPolicy("Permission", policy => +// policy.Requirements.Add(new PermissionAuthorizationRequirement())); +// }); var app = builder.Build(); diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/IPermissionAuthorizationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/IPermissionAuthorizationProvider.cs new file mode 100644 index 00000000..a6fa3b2a --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/IPermissionAuthorizationProvider.cs @@ -0,0 +1,6 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public interface IPermissionAuthorizationProvider +{ + IEnumerable GetPermissions(string userId); +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs new file mode 100644 index 00000000..837cda0c --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs @@ -0,0 +1,22 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public class Permission +{ + public class Operation + { + public const string Write = $"{nameof(Operation)}.{nameof(Write)}"; + public const string Read = $"{nameof(Operation)}.{nameof(Read)}"; + + private static readonly Lazy> s_values + = new(() => + { + return FieldTypeAssistant.GetStaticFieldName() + .ToDictionary(p => p.Key, + p => p.Value, + StringComparer.InvariantCultureIgnoreCase); + }); + + public static Dictionary GetValues() + => s_values.Value; + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs index ffb736a0..b1a9288c 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs @@ -4,24 +4,33 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization public class PermissionAuthorizationHandler : AuthorizationHandler { + private IPermissionAuthorizationProvider _authorizationProvider; - public override Task HandleAsync(AuthorizationHandlerContext context) + public PermissionAuthorizationHandler(IPermissionAuthorizationProvider authorizationProvider) { - return base.HandleAsync(context); + this._authorizationProvider = authorizationProvider; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement) { - if (context.User.Identity.IsAuthenticated==false) + if (context.User.Identity.IsAuthenticated == false) { - - // return Task.FromResult(false); + context.Fail(new AuthorizationFailureReason(this, $"目前請求沒有通過驗證")); + return; } - + var userId = context.User.Identity.Name; + var permissions = this._authorizationProvider.GetPermissions(userId); + if (permissions.Any(p => p.StartsWith(requirement.PolicyName, StringComparison.InvariantCultureIgnoreCase)) == + false) + { + context.Fail(new AuthorizationFailureReason(this, $"用戶 '{userId}',沒有授權 '{requirement.PolicyName}'")); + } - // Task.FromResult(false); + if (context.HasFailed == false) + { + context.Succeed(requirement); + } } - } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs index 578239d0..a60f2fa8 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs @@ -1,15 +1,54 @@ -using Microsoft.AspNetCore.Authorization; +using System.Text.Json; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization.Policy; namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; public class PermissionAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler { - public async Task HandleAsync(RequestDelegate next, + private readonly ILogger _logger; + + // private readonly AuthorizationMiddlewareResultHandler _defaultHandler = new(); + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public PermissionAuthorizationMiddlewareResultHandler( + ILogger logger, + JsonSerializerOptions jsonSerializerOptions) + { + this._logger = logger; + this._jsonSerializerOptions = jsonSerializerOptions; + } + + public async Task HandleAsync( + RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) { - await next(context); + var permissionAuthorizationRequirements = policy.Requirements.OfType(); + + if (authorizeResult.Forbidden + && permissionAuthorizationRequirements.Any()) + { + context.Response.StatusCode = 403; + this._logger.LogInformation("{AuthorizationFailureResults}", new + { + ErrorCode = "Invalid Authorization", + ErrorMessages = authorizeResult.AuthorizationFailure.FailureReasons + }); + + // 回傳前端模糊訊息 + await context.Response.WriteAsJsonAsync(new + { + ErrorCode = "Invalid Authorization", + ErrorMessages = new[] { "Please contact your administrator" } + // ErrorMessages = authorizeResult.AuthorizationFailure.FailureReasons + }, this._jsonSerializerOptions); + return; + } + + await next.Invoke(context); + + // await next(context); } } \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs new file mode 100644 index 00000000..63d1a91c --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; + +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +internal class PermissionAuthorizationPolicyProvider : IAuthorizationPolicyProvider +{ + const string POLICY_PREFIX = "Permission"; + + public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; } + + public PermissionAuthorizationPolicyProvider(IOptions options) + { + // ASP.NET Core only uses one authorization policy provider, so if the custom implementation + // doesn't handle all policies (including default policies, etc.) it should fall back to an + // alternate provider. + // + // In this sample, a default authorization policy provider (constructed with options from the + // dependency injection container) is used if this custom provider isn't able to handle a given + // policy name. + // + // If a custom policy provider is able to handle all expected policy names then, of course, this + // fallback pattern is unnecessary. + FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options); + } + + public Task GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync(); + + public Task GetFallbackPolicyAsync() => FallbackPolicyProvider.GetFallbackPolicyAsync(); + + // Policies are looked up by string name, so expect 'parameters' (like age) + // to be embedded in the policy names. This is abstracted away from developers + // by the more strongly-typed attributes derived from AuthorizeAttribute + // (like [MinimumAgeAuthorize] in this sample) + public Task GetPolicyAsync(string policyName) + { + var operationValues = Permission.Operation.GetValues(); + if (operationValues.Any(p => p.Key.StartsWith(policyName, StringComparison.InvariantCultureIgnoreCase))) + { + var policy = new AuthorizationPolicyBuilder(); + policy.AddRequirements(new PermissionAuthorizationRequirement + { + PolicyName = policyName + }); + return Task.FromResult(policy.Build()); + } + + // If the policy name doesn't match the format expected by this policy provider, + // try the fallback provider. If no fallback provider is used, this would return + // Task.FromResult(null) instead. + return FallbackPolicyProvider.GetPolicyAsync(policyName); + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationProvider.cs new file mode 100644 index 00000000..aa339fb4 --- /dev/null +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationProvider.cs @@ -0,0 +1,21 @@ +namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization; + +public class PermissionAuthorizationProvider : IPermissionAuthorizationProvider +{ + private readonly Dictionary> _clientPermissions = + new(StringComparer.InvariantCultureIgnoreCase) + { + { "yao", new[] { Permission.Operation.Read, Permission.Operation.Write } }, + { "jojo", new[] { Permission.Operation.Read} } + }; + + public IEnumerable GetPermissions(string userId) + { + if (this._clientPermissions.TryGetValue(userId, out var result) == false) + { + result = new List(); + } + + return result; + } +} \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs index 96f2e195..61888f90 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationRequirement.cs @@ -4,4 +4,5 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization public class PermissionAuthorizationRequirement : IAuthorizationRequirement { + public string PolicyName { get; init; } } \ No newline at end of file From 1ae04203c1afc5789315efa5187366f381c73ea3 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 21 Jun 2022 23:39:49 +0800 Subject: [PATCH 232/267] refactor --- .../Security/Authorization/Permission.cs | 4 ++-- .../Security/Authorization/PermissionAuthorizationHandler.cs | 2 +- .../PermissionAuthorizationMiddlewareResultHandler.cs | 1 - .../Authorization/PermissionAuthorizationPolicyProvider.cs | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs index 837cda0c..5af9af80 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/Permission.cs @@ -4,8 +4,8 @@ public class Permission { public class Operation { - public const string Write = $"{nameof(Operation)}.{nameof(Write)}"; - public const string Read = $"{nameof(Operation)}.{nameof(Read)}"; + public const string Write = $"{nameof(Permission)}.{nameof(Operation)}:{nameof(Write)}"; + public const string Read = $"{nameof(Permission)}.{nameof(Operation)}:{nameof(Read)}"; private static readonly Lazy> s_values = new(() => diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs index b1a9288c..fed3f092 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationHandler.cs @@ -4,7 +4,7 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization public class PermissionAuthorizationHandler : AuthorizationHandler { - private IPermissionAuthorizationProvider _authorizationProvider; + private readonly IPermissionAuthorizationProvider _authorizationProvider; public PermissionAuthorizationHandler(IPermissionAuthorizationProvider authorizationProvider) { diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs index a60f2fa8..03e594a0 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationMiddlewareResultHandler.cs @@ -8,7 +8,6 @@ public class PermissionAuthorizationMiddlewareResultHandler : IAuthorizationMidd { private readonly ILogger _logger; - // private readonly AuthorizationMiddlewareResultHandler _defaultHandler = new(); private readonly JsonSerializerOptions _jsonSerializerOptions; public PermissionAuthorizationMiddlewareResultHandler( diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs index 63d1a91c..774f1094 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authorization/PermissionAuthorizationPolicyProvider.cs @@ -5,8 +5,6 @@ namespace Lab.AspNetCore.Security.BasicAuthenticationSite.Security.Authorization internal class PermissionAuthorizationPolicyProvider : IAuthorizationPolicyProvider { - const string POLICY_PREFIX = "Permission"; - public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; } public PermissionAuthorizationPolicyProvider(IOptions options) From 3499b66d07fb4d1e00582697039d2b2a73201eb7 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 26 Jun 2022 13:53:00 +0800 Subject: [PATCH 233/267] =?UTF-8?q?feat:=20=E5=A5=97=E7=94=A8=E7=AF=84?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../K8sTemplateEngineTests.cs | 28 +++++++++++++++++++ .../Lab.RazorTemplate.Test.csproj | 22 +++++++++++++++ .../Lab.RazorTemplate.Test/Usings.cs | 1 + .../Lab.RazorTemplate/Lab.RazorTemplate.sln | 22 +++++++++++++++ .../Lab.RazorTemplate/K8sTemplateEngine.cs | 15 ++++++++++ .../Lab.RazorTemplate/K8sValue.cs | 22 +++++++++++++++ .../Lab.RazorTemplate.csproj | 15 ++++++++++ .../Template.ConfigMap.cshtml | 9 ++++++ .../BasicAuthenticationHandler.cs | 12 ++++++++ 9 files changed, 146 insertions(+) create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Lab.RazorTemplate.Test.csproj create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Usings.cs create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate.sln create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sTemplateEngine.cs create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sValue.cs create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/Lab.RazorTemplate.csproj create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/Template.ConfigMap.cshtml diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs new file mode 100644 index 00000000..f001f3a6 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs @@ -0,0 +1,28 @@ +namespace Lab.RazorTemplate.Test; + +[TestClass] +public class K8sTemplateEngineTests +{ + [TestMethod] + public async Task 替換範本() + { + var templatePath = "Template.ConfigMap.cshtml"; + var k8sValue = new K8sValue() + { + Common = new Common + { + ProjectName = "member-service-api", + Namespace = "member-service", + }, + }; + var k8sDynamicValues = new Dictionary + { + ["Value1"] = "1", + ["Value2"] = "2", + ["K8S_COMMON_SERVICE_NAME"] = "3", + }; + var engine = new K8sTemplateEngine(); + var result = await engine.RenderAsync(templatePath, k8sValue, k8sDynamicValues); + Console.WriteLine(result); + } +} \ No newline at end of file diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Lab.RazorTemplate.Test.csproj b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Lab.RazorTemplate.Test.csproj new file mode 100644 index 00000000..54419e07 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Lab.RazorTemplate.Test.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Usings.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Usings.cs new file mode 100644 index 00000000..ab67c7ea --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/Usings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.sln b/Template/Lab.RazorTemplate/Lab.RazorTemplate.sln new file mode 100644 index 00000000..cc567ad9 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.RazorTemplate", "Lab.RazorTemplate\Lab.RazorTemplate.csproj", "{4AC18C9C-3D58-4CD8-99D2-EA5C1ED9B1EA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.RazorTemplate.Test", "Lab.RazorTemplate.Test\Lab.RazorTemplate.Test.csproj", "{9FA27B86-E221-4D17-866F-DE5B2ED983CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4AC18C9C-3D58-4CD8-99D2-EA5C1ED9B1EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AC18C9C-3D58-4CD8-99D2-EA5C1ED9B1EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AC18C9C-3D58-4CD8-99D2-EA5C1ED9B1EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AC18C9C-3D58-4CD8-99D2-EA5C1ED9B1EA}.Release|Any CPU.Build.0 = Release|Any CPU + {9FA27B86-E221-4D17-866F-DE5B2ED983CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FA27B86-E221-4D17-866F-DE5B2ED983CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FA27B86-E221-4D17-866F-DE5B2ED983CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FA27B86-E221-4D17-866F-DE5B2ED983CA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sTemplateEngine.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sTemplateEngine.cs new file mode 100644 index 00000000..14e417b1 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sTemplateEngine.cs @@ -0,0 +1,15 @@ +using System.Text; + +namespace Lab.RazorTemplate; + +public class K8sTemplateEngine +{ + public async Task RenderAsync(string templatePath, + K8sValue k8sValue, + Dictionary k8sDynamicValue) + { + return await Razor.Templating.Core.RazorTemplateEngine.RenderAsync(templatePath, + k8sValue, + k8sDynamicValue); + } +} diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sValue.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sValue.cs new file mode 100644 index 00000000..795e6fd8 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/K8sValue.cs @@ -0,0 +1,22 @@ +namespace Lab.RazorTemplate; + +public class K8sValue +{ + public Common Common { get; set; } + + public Resource Resource { get; set; } +} + +public class Common +{ + public string ProjectName { get; set; } + + public string Namespace { get; set; } +} + +public class Resource +{ + public uint CPU { get; set; } + + public uint Memory { get; set; } +} diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/Lab.RazorTemplate.csproj b/Template/Lab.RazorTemplate/Lab.RazorTemplate/Lab.RazorTemplate.csproj new file mode 100644 index 00000000..41c69183 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/Lab.RazorTemplate.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + true + + + + + + + + diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/Template.ConfigMap.cshtml b/Template/Lab.RazorTemplate/Lab.RazorTemplate/Template.ConfigMap.cshtml new file mode 100644 index 00000000..348b1de3 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/Template.ConfigMap.cshtml @@ -0,0 +1,9 @@ +@model Lab.RazorTemplate.K8sValue +apiVersion: v1 +kind: ConfigMap +metadata: +name: @Model.Common.ProjectName +namespace: @Model.Common.Namespace +spec1: @ViewData["Value1"] +spec2: @ViewBag.Value2 +spec3: @ViewBag.K8S_COMMON_SERVICE_NAME \ No newline at end of file diff --git a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs index 5f85a0e1..41f3d02b 100644 --- a/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs +++ b/WebAPI/Security/Lab.AspNetCore.Security/Lab.AspNetCore.Security.BasicAuthenticationSite/Security/Authentication/BasicAuthenticationHandler.cs @@ -26,6 +26,18 @@ public BasicAuthenticationHandler( this._authenticationProvider = authenticationProvider; } + /// + /// + /// + void CreateTestServer() + { + + } + + void CreateTask() + { + + } protected override async Task HandleAuthenticateAsync() { var schemeName = this.Scheme.Name; //由外部注入 From bc3728a9c0af2f19f2fdae0ea875ad2505727332 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 26 Jun 2022 23:21:04 +0800 Subject: [PATCH 234/267] feat: add other temp --- .../K8sTemplateEngineTests.cs | 30 ++++++++++++++++++ .../Lab.RazorTemplate/EnvTemplate.cshtml | 31 +++++++++++++++++++ .../Lab.RazorTemplate/EnvironmentType.cs | 9 ++++++ .../Lab.RazorTemplate/MarketType.cs | 8 +++++ 4 files changed, 78 insertions(+) create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvironmentType.cs create mode 100644 Template/Lab.RazorTemplate/Lab.RazorTemplate/MarketType.cs diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs index f001f3a6..4ddae473 100644 --- a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs @@ -1,3 +1,6 @@ +using System.Net; +using System.Text; + namespace Lab.RazorTemplate.Test; [TestClass] @@ -21,6 +24,33 @@ public async Task 替換範本() ["Value2"] = "2", ["K8S_COMMON_SERVICE_NAME"] = "3", }; + + var engine = new K8sTemplateEngine(); + var result = await engine.RenderAsync(templatePath, k8sValue, k8sDynamicValues); + Console.WriteLine(result); + } + + [TestMethod] + public async Task 替換範本aa() + { + var templatePath = "EnvTemplate.cshtml"; + var k8sValue = new K8sValue() + { + Common = new Common + { + ProjectName = "member-service-api", + Namespace = "member-service", + }, + }; + var k8sDynamicValues = new Dictionary + { + ["Market"] = "TW", + ["Environment"] = "Dev", + }; + var response = new HttpResponseMessage(); + response.StatusCode = HttpStatusCode.NoContent; + response.Content = new StringContent("[]", Encoding.UTF8); + var engine = new K8sTemplateEngine(); var result = await engine.RenderAsync(templatePath, k8sValue, k8sDynamicValues); Console.WriteLine(result); diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml new file mode 100644 index 00000000..9e599b98 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml @@ -0,0 +1,31 @@ +@using Lab.RazorTemplate +@model Lab.RazorTemplate.K8sValue +@{ + var _market = ViewBag.Market; + var _environment = ViewBag.Environment; + Console.WriteLine(_environment.ToString()); +} +@{ + var K8S_COMMON_SERVICE_NAME = new Dictionary() + { + { MarketName.TW, "k8s-common-tw" }, + { MarketName.HK, "k8s-common-hk" }, + { MarketName.MY, "k8s-common-my" }, + }; + Console.WriteLine($"{nameof(K8S_COMMON_SERVICE_NAME)} = {K8S_COMMON_SERVICE_NAME}"); +} +K8S_COMMON_SERVICE_NAME= @K8S_COMMON_SERVICE_NAME[_market] +@{ + string NMQ_APIMIN_BASE_URL = null; + if (_environment == EnvironmentName.Dev) + { + NMQ_APIMIN_BASE_URL = "ABC"; + } + else if (_environment == EnvironmentName.QA + && _market == MarketName.TW) + { + NMQ_APIMIN_BASE_URL = "DEF"; + } + Console.WriteLine($"{nameof(NMQ_APIMIN_BASE_URL)} = {NMQ_APIMIN_BASE_URL}"); +} +NMQ_APIMIN_BASE_URL = @NMQ_APIMIN_BASE_URL; \ No newline at end of file diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvironmentType.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvironmentType.cs new file mode 100644 index 00000000..0cf65b4a --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvironmentType.cs @@ -0,0 +1,9 @@ +namespace Lab.RazorTemplate; + +public class EnvironmentName +{ + public const string Dev = "Dev"; + public const string Test = "Test"; + public const string QA = "QA"; + public const string Production = "Prod"; +} \ No newline at end of file diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/MarketType.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate/MarketType.cs new file mode 100644 index 00000000..5948fd30 --- /dev/null +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/MarketType.cs @@ -0,0 +1,8 @@ +namespace Lab.RazorTemplate; + +public class MarketName +{ + public const string TW = "TW"; + public const string MY = "MY"; + public const string HK = "HK"; +} \ No newline at end of file From 5e786ca21cc759c17aa5e217cb1cbccf31b40a5d Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 26 Jun 2022 23:30:02 +0800 Subject: [PATCH 235/267] refactor --- .../K8sTemplateEngineTests.cs | 19 +++++-------------- .../Lab.RazorTemplate/EnvTemplate.cshtml | 3 ++- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs index 4ddae473..d8b241c1 100644 --- a/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate.Test/K8sTemplateEngineTests.cs @@ -27,32 +27,23 @@ public async Task 替換範本() var engine = new K8sTemplateEngine(); var result = await engine.RenderAsync(templatePath, k8sValue, k8sDynamicValues); - Console.WriteLine(result); + Console.WriteLine($"Render Result:\r\n{result}"); } [TestMethod] - public async Task 替換範本aa() + public async Task 替換範本_1() { var templatePath = "EnvTemplate.cshtml"; - var k8sValue = new K8sValue() - { - Common = new Common - { - ProjectName = "member-service-api", - Namespace = "member-service", - }, - }; + var k8sValue = new K8sValue(); var k8sDynamicValues = new Dictionary { ["Market"] = "TW", ["Environment"] = "Dev", }; - var response = new HttpResponseMessage(); - response.StatusCode = HttpStatusCode.NoContent; - response.Content = new StringContent("[]", Encoding.UTF8); var engine = new K8sTemplateEngine(); var result = await engine.RenderAsync(templatePath, k8sValue, k8sDynamicValues); - Console.WriteLine(result); + Console.WriteLine(); + Console.WriteLine($"Render Result:\r\n{result}"); } } \ No newline at end of file diff --git a/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml index 9e599b98..e039e1a2 100644 --- a/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml +++ b/Template/Lab.RazorTemplate/Lab.RazorTemplate/EnvTemplate.cshtml @@ -3,7 +3,8 @@ @{ var _market = ViewBag.Market; var _environment = ViewBag.Environment; - Console.WriteLine(_environment.ToString()); + Console.WriteLine($"{nameof(_market)} = {_market}"); + Console.WriteLine($"{nameof(_environment)} = {_environment}"); } @{ var K8S_COMMON_SERVICE_NAME = new Dictionary() From 4f887189c91d27b8b9b713a699bf417906edcccb Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 9 Jul 2022 00:15:13 +0800 Subject: [PATCH 236/267] add minio s3 sample --- .../Lab.Aws.S3.MinIOS3.csproj | 19 ++++++++++++++ AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs | 25 +++++++++++++++++++ AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Usings.cs | 1 + AWS/Lab.AwsS3/Lab.AwsS3.sln | 21 ++++++++++++++++ AWS/Lab.AwsS3/docker-compose.yaml | 17 +++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Lab.Aws.S3.MinIOS3.csproj create mode 100644 AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs create mode 100644 AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Usings.cs create mode 100644 AWS/Lab.AwsS3/Lab.AwsS3.sln create mode 100644 AWS/Lab.AwsS3/docker-compose.yaml diff --git a/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Lab.Aws.S3.MinIOS3.csproj b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Lab.Aws.S3.MinIOS3.csproj new file mode 100644 index 00000000..547f14e9 --- /dev/null +++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Lab.Aws.S3.MinIOS3.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + diff --git a/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs new file mode 100644 index 00000000..6a096e8b --- /dev/null +++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs @@ -0,0 +1,25 @@ +using Amazon; +using Amazon.S3; +using Amazon.S3.Model; + +namespace Lab.Aws.S3.MinIOS3; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public async Task 新增一個儲存桶() + { + var s3Config = new AmazonS3Config() + { + RegionEndpoint = RegionEndpoint.USEast1, + ServiceURL = "http://localhost:9000", + ForcePathStyle = true + }; + var s3Client = new AmazonS3Client(s3Config); + var response = await s3Client.PutBucketAsync(new PutBucketRequest + { + BucketName = "test-bucket", + }); + } +} \ No newline at end of file diff --git a/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Usings.cs b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Usings.cs new file mode 100644 index 00000000..ab67c7ea --- /dev/null +++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Usings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/AWS/Lab.AwsS3/Lab.AwsS3.sln b/AWS/Lab.AwsS3/Lab.AwsS3.sln new file mode 100644 index 00000000..c4a97c99 --- /dev/null +++ b/AWS/Lab.AwsS3/Lab.AwsS3.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Aws.S3.MinIOS3", "Lab.Aws.S3.MinIOS3\Lab.Aws.S3.MinIOS3.csproj", "{0FF96B3C-2410-4444-A113-FB2B80E4D940}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{17F77AC4-5953-4CD1-917D-28A4746AB759}" + ProjectSection(SolutionItems) = preProject + docker-compose.yaml = docker-compose.yaml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0FF96B3C-2410-4444-A113-FB2B80E4D940}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FF96B3C-2410-4444-A113-FB2B80E4D940}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FF96B3C-2410-4444-A113-FB2B80E4D940}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FF96B3C-2410-4444-A113-FB2B80E4D940}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AWS/Lab.AwsS3/docker-compose.yaml b/AWS/Lab.AwsS3/docker-compose.yaml new file mode 100644 index 00000000..018232a7 --- /dev/null +++ b/AWS/Lab.AwsS3/docker-compose.yaml @@ -0,0 +1,17 @@ +version: "3.8" + +services: + s3-minio: + container_name: "s3-minio" + hostname: "minio" + image: minio/minio:latest + volumes: + - ./minio/data:/data + ports: + - "9000:9000" + - "9001:9001" + environment: + # 這裡的 key 要跟 .aws/credentials 裡的 key 名稱一樣,aws cli 才能正常的運作 + MINIO_ROOT_USER: "AKIAIOSFODNN7EXAMPLE" + MINIO_ROOT_PASSWORD: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + command: server --console-address :9001 /data \ No newline at end of file From b1f55a106844ca7a5a1dfe0e3918f656beae7bc1 Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 14 Jul 2022 10:12:29 +0800 Subject: [PATCH 237/267] feat: add yaml and web project --- .../Lab.SpecFirst/AutoGenerated/Controller.cs | 137 +++++ .../Swagger/Lab.SpecFirst/Lab.SpecFirst.sln | 39 ++ WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml | 15 + WebAPI/Swagger/Lab.SpecFirst/doc/index.yaml | 112 ++++ .../AutoGenerated/LabSpecClient.cs | 531 ++++++++++++++++++ .../Lab.SpecFirst.Adapter.csproj | 9 + .../src/Lab.SpecFirst.Web/.dockerignore | 25 + .../AutoGenerated/Controller.cs | 137 +++++ .../Controllers/WeatherForecastController.cs | 32 ++ .../src/Lab.SpecFirst.Web/Dockerfile | 20 + .../Lab.SpecFirst.Web.csproj | 14 + .../src/Lab.SpecFirst.Web/Program.cs | 26 + .../Properties/launchSettings.json | 31 + .../src/Lab.SpecFirst.Web/WeatherForecast.cs | 12 + .../appsettings.Development.json | 8 + .../src/Lab.SpecFirst.Web/appsettings.json | 9 + 16 files changed, 1157 insertions(+) create mode 100644 WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/Lab.SpecFirst.sln create mode 100644 WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml create mode 100644 WebAPI/Swagger/Lab.SpecFirst/doc/index.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/.dockerignore create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/WeatherForecastController.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Dockerfile create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Properties/launchSettings.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/WeatherForecast.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.Development.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.json diff --git a/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs new file mode 100644 index 00000000..bb7137b4 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs @@ -0,0 +1,137 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Web +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public interface ILab.SpecFirst.WebController + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// Create a pet + /// Null response + System.Threading.Tasks.Task CreatePetsAsync(); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + [Microsoft.AspNetCore.Mvc.Route("api/")] + public partial class Lab.SpecFirst.WebController : Microsoft.AspNetCore.Mvc.ControllerBase + { + private ILab.SpecFirst.WebController _implementation; + + public Lab.SpecFirst.WebController(ILab.SpecFirst.WebController implementation) + { + _implementation = implementation; + } + + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task> ListPets([Microsoft.AspNetCore.Mvc.FromQuery] int? limit) + { + return _implementation.ListPetsAsync(limit); + } + + /// Create a pet + /// Null response + [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task CreatePets() + { + return _implementation.CreatePetsAsync(); + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets/{petId}")] + public System.Threading.Tasks.Task ShowPetById(string petId) + { + return _implementation.ShowPetByIdAsync(petId); + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + public long Id { get; set; } + + [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [Newtonsoft.Json.JsonProperty("tag", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.Always)] + public int Code { get; set; } + + [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst/Lab.SpecFirst.sln new file mode 100644 index 00000000..9b8579aa --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/Lab.SpecFirst.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1C8C1BD9-1338-47A0-963B-D35B1AD07476}" + ProjectSection(SolutionItems) = preProject + Taskfile.yml = Taskfile.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{7C405A84-132F-43F2-9F97-388CC40BED1D}" + ProjectSection(SolutionItems) = preProject + doc\index.yaml = doc\index.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F900DCE0-EF45-4629-AA24-949E65BAB714}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Web", "src\Lab.SpecFirst.Web\Lab.SpecFirst.Web.csproj", "{70299524-F9F4-41DF-80EF-D1CE03C2965A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Adapter", "src\Lab.SpecFirst.Adapter\Lab.SpecFirst.Adapter.csproj", "{F5AAACC5-781F-41E6-8CD5-39389A00942C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7C405A84-132F-43F2-9F97-388CC40BED1D} = {1C8C1BD9-1338-47A0-963B-D35B1AD07476} + {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + EndGlobalSection +EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml new file mode 100644 index 00000000..ed4d14b1 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml @@ -0,0 +1,15 @@ +version: "3" + +dotenv: [ "secrets/secrets.env" ] + +tasks: + rest-codegen-client: + desc: 產生 Client Code + cmds: + - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false + + + rest-codegen-server: + desc: 產生 Server Code + cmds: + - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstConteoller /namespace:Lab.SpecFirst.Web /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson diff --git a/WebAPI/Swagger/Lab.SpecFirst/doc/index.yaml b/WebAPI/Swagger/Lab.SpecFirst/doc/index.yaml new file mode 100644 index 00000000..565bfc49 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/doc/index.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs new file mode 100644 index 00000000..55ae054c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs @@ -0,0 +1,531 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Adapter +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial interface ILabSpecClient + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken); + + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class LabSpecClient : ILabSpecClient + { + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public LabSpecClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public System.Threading.Tasks.Task> ListPetsAsync(int? limit) + { + return ListPetsAsync(limit, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public async System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets?"); + if (limit != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("limit") + "=").Append(System.Uri.EscapeDataString(ConvertToString(limit, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create a pet + /// Null response + /// A server side error occurred. + public System.Threading.Tasks.Task CreatePetsAsync() + { + return CreatePetsAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + public async System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public System.Threading.Tasks.Task ShowPetByIdAsync(string petId) + { + return ShowPetByIdAsync(petId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public async System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken) + { + if (petId == null) + throw new System.ArgumentNullException("petId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets/{petId}"); + urlBuilder_.Replace("{petId}", System.Uri.EscapeDataString(ConvertToString(petId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/.dockerignore b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/.dockerignore new file mode 100644 index 00000000..cd967fc3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs new file mode 100644 index 00000000..eee39071 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -0,0 +1,137 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Web +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public interface ISpecFirstConteollerController + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// Create a pet + /// Null response + System.Threading.Tasks.Task CreatePetsAsync(); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + [Microsoft.AspNetCore.Mvc.Route("api/")] + public partial class SpecFirstConteollerController : Microsoft.AspNetCore.Mvc.ControllerBase + { + private ISpecFirstConteollerController _implementation; + + public SpecFirstConteollerController(ISpecFirstConteollerController implementation) + { + _implementation = implementation; + } + + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task> ListPets([Microsoft.AspNetCore.Mvc.FromQuery] int? limit) + { + return _implementation.ListPetsAsync(limit); + } + + /// Create a pet + /// Null response + [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task CreatePets() + { + return _implementation.CreatePetsAsync(); + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets/{petId}")] + public System.Threading.Tasks.Task ShowPetById(string petId) + { + return _implementation.ShowPetByIdAsync(petId); + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/WeatherForecastController.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..f0631a40 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.SpecFirst.Web.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Dockerfile b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Dockerfile new file mode 100644 index 00000000..e568de25 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj", "Lab.SpecFirst.Web/"] +RUN dotnet restore "src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj" +COPY . . +WORKDIR "/src/Lab.SpecFirst.Web" +RUN dotnet build "Lab.SpecFirst.Web.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Lab.SpecFirst.Web.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Lab.SpecFirst.Web.dll"] diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj new file mode 100644 index 00000000..0db9dec4 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs new file mode 100644 index 00000000..329fe361 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs @@ -0,0 +1,26 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Properties/launchSettings.json b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Properties/launchSettings.json new file mode 100644 index 00000000..466bd7be --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:9860", + "sslPort": 44313 + } + }, + "profiles": { + "Lab.SpecFirst.Web": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7041;http://localhost:5041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/WeatherForecast.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/WeatherForecast.cs new file mode 100644 index 00000000..bacb51a3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Lab.SpecFirst.Web; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.Development.json b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.json b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From 4c277b6d70db1fc2f194834ba06a8449550f2e2b Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 17 Jul 2022 00:04:45 +0800 Subject: [PATCH 238/267] add task --- WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml index ed4d14b1..a2fa967f 100644 --- a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml @@ -3,12 +3,16 @@ version: "3" dotenv: [ "secrets/secrets.env" ] tasks: + rest-codegen-code: + cmds: + - task: rest-codegen-client + - task: rest-codegen-server + rest-codegen-client: desc: 產生 Client Code cmds: - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false - rest-codegen-server: desc: 產生 Server Code cmds: From 51428f41f78bb653c1d75774f30faa8776540c84 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 17 Jul 2022 00:20:17 +0800 Subject: [PATCH 239/267] refactor --- WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml index a2fa967f..6913848c 100644 --- a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml @@ -4,6 +4,7 @@ dotenv: [ "secrets/secrets.env" ] tasks: rest-codegen-code: + desc: 產生 Client / Server Code cmds: - task: rest-codegen-client - task: rest-codegen-server From 57391bd35703ad6ee9caa3dccf69f09f16d18359 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 17 Jul 2022 00:25:52 +0800 Subject: [PATCH 240/267] remove folder --- .../Lab.SpecFirst/AutoGenerated/Controller.cs | 137 ------------------ 1 file changed, 137 deletions(-) delete mode 100644 WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs diff --git a/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs deleted file mode 100644 index bb7137b4..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst/AutoGenerated/Controller.cs +++ /dev/null @@ -1,137 +0,0 @@ -//---------------------- -// -// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) -// -//---------------------- - -#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." -#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." -#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' -#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... -#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." -#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" -#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" - -namespace Lab.SpecFirst.Web -{ - using System = global::System; - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] - public interface ILab.SpecFirst.WebController - { - /// List all pets - /// How many items to return at one time (max 100) - /// A paged array of pets - System.Threading.Tasks.Task> ListPetsAsync(int? limit); - - /// Create a pet - /// Null response - System.Threading.Tasks.Task CreatePetsAsync(); - - /// Info for a specific pet - /// The id of the pet to retrieve - /// Expected response to a valid request - System.Threading.Tasks.Task ShowPetByIdAsync(string petId); - - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] - [Microsoft.AspNetCore.Mvc.Route("api/")] - public partial class Lab.SpecFirst.WebController : Microsoft.AspNetCore.Mvc.ControllerBase - { - private ILab.SpecFirst.WebController _implementation; - - public Lab.SpecFirst.WebController(ILab.SpecFirst.WebController implementation) - { - _implementation = implementation; - } - - /// List all pets - /// How many items to return at one time (max 100) - /// A paged array of pets - [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets")] - public System.Threading.Tasks.Task> ListPets([Microsoft.AspNetCore.Mvc.FromQuery] int? limit) - { - return _implementation.ListPetsAsync(limit); - } - - /// Create a pet - /// Null response - [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("pets")] - public System.Threading.Tasks.Task CreatePets() - { - return _implementation.CreatePetsAsync(); - } - - /// Info for a specific pet - /// The id of the pet to retrieve - /// Expected response to a valid request - [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets/{petId}")] - public System.Threading.Tasks.Task ShowPetById(string petId) - { - return _implementation.ShowPetByIdAsync(petId); - } - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pet - { - [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] - public long Id { get; set; } - - [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Name { get; set; } - - [Newtonsoft.Json.JsonProperty("tag", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string Tag { get; set; } - - private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); - - [Newtonsoft.Json.JsonExtensionData] - public System.Collections.Generic.IDictionary AdditionalProperties - { - get { return _additionalProperties; } - set { _additionalProperties = value; } - } - - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pets : System.Collections.ObjectModel.Collection - { - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Error - { - [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.Always)] - public int Code { get; set; } - - [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Message { get; set; } - - private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); - - [Newtonsoft.Json.JsonExtensionData] - public System.Collections.Generic.IDictionary AdditionalProperties - { - get { return _additionalProperties; } - set { _additionalProperties = value; } - } - - - } - -} - -#pragma warning restore 1591 -#pragma warning restore 1573 -#pragma warning restore 472 -#pragma warning restore 114 -#pragma warning restore 108 -#pragma warning restore 3016 \ No newline at end of file From d06f4bb992178313339ffb8bc891dd5aa31fedd8 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 17 Jul 2022 02:01:49 +0800 Subject: [PATCH 241/267] impl controlle --- WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml | 2 +- .../AutoGenerated/Controller.cs | 10 ++--- .../Controllers/SpecFirstController.cs | 38 +++++++++++++++++++ .../src/Lab.SpecFirst.Web/Program.cs | 5 ++- 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs diff --git a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml index 6913848c..61cb3a6a 100644 --- a/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst/Taskfile.yml @@ -17,4 +17,4 @@ tasks: rest-codegen-server: desc: 產生 Server Code cmds: - - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstConteoller /namespace:Lab.SpecFirst.Web /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson + - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs index eee39071..c458dd95 100644 --- a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -12,12 +12,12 @@ #pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" #pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" -namespace Lab.SpecFirst.Web +namespace Lab.SpecFirst.Web.Controllers { using System = global::System; [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] - public interface ISpecFirstConteollerController + public interface ISpecFirstContractController { /// List all pets /// How many items to return at one time (max 100) @@ -37,11 +37,11 @@ public interface ISpecFirstConteollerController [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] [Microsoft.AspNetCore.Mvc.Route("api/")] - public partial class SpecFirstConteollerController : Microsoft.AspNetCore.Mvc.ControllerBase + public partial class SpecFirstContractController : Microsoft.AspNetCore.Mvc.ControllerBase { - private ISpecFirstConteollerController _implementation; + private ISpecFirstContractController _implementation; - public SpecFirstConteollerController(ISpecFirstConteollerController implementation) + public SpecFirstContractController(ISpecFirstContractController implementation) { _implementation = implementation; } diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs new file mode 100644 index 00000000..eed7b89a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs @@ -0,0 +1,38 @@ +namespace Lab.SpecFirst.Web.Controllers; + +class SpecFirstController : ISpecFirstContractController +{ + public async Task> ListPetsAsync(int? limit) + { + return new List() + { + new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }, + }; + } + + public async Task CreatePetsAsync() + { + + } + + public async Task ShowPetByIdAsync(string petId) + { + return new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs index 329fe361..8f8bd1ae 100644 --- a/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs +++ b/WebAPI/Swagger/Lab.SpecFirst/src/Lab.SpecFirst.Web/Program.cs @@ -1,7 +1,10 @@ +using Lab.SpecFirst.Web.Controllers; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +builder.Services.AddScoped(); +// Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle From 87721cc8173e7e6971a4685223ae21eb8a50487e Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 00:59:26 +0800 Subject: [PATCH 242/267] from fork lab.specfirst --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 44 ++ WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 20 + .../doc/components/parameter - Copy (2).yaml | 112 ++++ .../doc/components/parameter - Copy.yaml | 112 ++++ .../doc/components/parameter.yaml | 112 ++++ WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml | 112 ++++ .../Lab.SpecFirst2/doc/schemas/index.yaml | 19 + .../AutoGenerated/LabSpecClient.cs | 531 ++++++++++++++++++ .../Lab.SpecFirst.Adapter.csproj | 9 + .../src/Lab.SpecFirst.Web/.dockerignore | 25 + .../AutoGenerated/Controller.cs | 137 +++++ .../Controllers/SpecFirstController.cs | 38 ++ .../src/Lab.SpecFirst.Web/Dockerfile | 20 + .../Lab.SpecFirst.Web.csproj | 14 + .../src/Lab.SpecFirst.Web/Program.cs | 29 + .../Properties/launchSettings.json | 31 + .../src/Lab.SpecFirst.Web/WeatherForecast.cs | 12 + .../appsettings.Development.json | 8 + .../src/Lab.SpecFirst.Web/appsettings.json | 9 + 19 files changed, 1394 insertions(+) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln new file mode 100644 index 00000000..70b4eaae --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1C8C1BD9-1338-47A0-963B-D35B1AD07476}" + ProjectSection(SolutionItems) = preProject + Taskfile.yml = Taskfile.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{7C405A84-132F-43F2-9F97-388CC40BED1D}" + ProjectSection(SolutionItems) = preProject + doc\index.yaml = doc\index.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F900DCE0-EF45-4629-AA24-949E65BAB714}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Web", "src\Lab.SpecFirst.Web\Lab.SpecFirst.Web.csproj", "{70299524-F9F4-41DF-80EF-D1CE03C2965A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Adapter", "src\Lab.SpecFirst.Adapter\Lab.SpecFirst.Adapter.csproj", "{F5AAACC5-781F-41E6-8CD5-39389A00942C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A302F-7CB9-411A-A189-33919965A007}" + ProjectSection(SolutionItems) = preProject + doc\schemas\index.yaml = doc\schemas\index.yaml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + EndGlobalSection +EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml new file mode 100644 index 00000000..61cb3a6a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -0,0 +1,20 @@ +version: "3" + +dotenv: [ "secrets/secrets.env" ] + +tasks: + rest-codegen-code: + desc: 產生 Client / Server Code + cmds: + - task: rest-codegen-client + - task: rest-codegen-server + + rest-codegen-client: + desc: 產生 Client Code + cmds: + - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false + + rest-codegen-server: + desc: 產生 Server Code + cmds: + - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml new file mode 100644 index 00000000..48e6371b --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml @@ -0,0 +1,19 @@ +parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + +schemas: + +requestBodies: +responses: + okResponse: + type: object + properties: + data: + example: ok + type: string diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs new file mode 100644 index 00000000..55ae054c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs @@ -0,0 +1,531 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Adapter +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial interface ILabSpecClient + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken); + + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class LabSpecClient : ILabSpecClient + { + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public LabSpecClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public System.Threading.Tasks.Task> ListPetsAsync(int? limit) + { + return ListPetsAsync(limit, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public async System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets?"); + if (limit != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("limit") + "=").Append(System.Uri.EscapeDataString(ConvertToString(limit, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create a pet + /// Null response + /// A server side error occurred. + public System.Threading.Tasks.Task CreatePetsAsync() + { + return CreatePetsAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + public async System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public System.Threading.Tasks.Task ShowPetByIdAsync(string petId) + { + return ShowPetByIdAsync(petId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public async System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken) + { + if (petId == null) + throw new System.ArgumentNullException("petId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets/{petId}"); + urlBuilder_.Replace("{petId}", System.Uri.EscapeDataString(ConvertToString(petId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore new file mode 100644 index 00000000..cd967fc3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs new file mode 100644 index 00000000..c458dd95 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -0,0 +1,137 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Web.Controllers +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public interface ISpecFirstContractController + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// Create a pet + /// Null response + System.Threading.Tasks.Task CreatePetsAsync(); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + [Microsoft.AspNetCore.Mvc.Route("api/")] + public partial class SpecFirstContractController : Microsoft.AspNetCore.Mvc.ControllerBase + { + private ISpecFirstContractController _implementation; + + public SpecFirstContractController(ISpecFirstContractController implementation) + { + _implementation = implementation; + } + + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task> ListPets([Microsoft.AspNetCore.Mvc.FromQuery] int? limit) + { + return _implementation.ListPetsAsync(limit); + } + + /// Create a pet + /// Null response + [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task CreatePets() + { + return _implementation.CreatePetsAsync(); + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets/{petId}")] + public System.Threading.Tasks.Task ShowPetById(string petId) + { + return _implementation.ShowPetByIdAsync(petId); + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs new file mode 100644 index 00000000..eed7b89a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs @@ -0,0 +1,38 @@ +namespace Lab.SpecFirst.Web.Controllers; + +class SpecFirstController : ISpecFirstContractController +{ + public async Task> ListPetsAsync(int? limit) + { + return new List() + { + new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }, + }; + } + + public async Task CreatePetsAsync() + { + + } + + public async Task ShowPetByIdAsync(string petId) + { + return new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile new file mode 100644 index 00000000..e568de25 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj", "Lab.SpecFirst.Web/"] +RUN dotnet restore "src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj" +COPY . . +WORKDIR "/src/Lab.SpecFirst.Web" +RUN dotnet build "Lab.SpecFirst.Web.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Lab.SpecFirst.Web.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Lab.SpecFirst.Web.dll"] diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj new file mode 100644 index 00000000..0db9dec4 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs new file mode 100644 index 00000000..8f8bd1ae --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs @@ -0,0 +1,29 @@ +using Lab.SpecFirst.Web.Controllers; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddScoped(); + +// Add services to the container. +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json new file mode 100644 index 00000000..466bd7be --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:9860", + "sslPort": 44313 + } + }, + "profiles": { + "Lab.SpecFirst.Web": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7041;http://localhost:5041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs new file mode 100644 index 00000000..bacb51a3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Lab.SpecFirst.Web; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From e9f6b44d9a12fbdce1e946acd6fa49b969f74aa5 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 10:02:56 +0800 Subject: [PATCH 243/267] feat: extra schema module --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 8 ++ .../Lab.SpecFirst2/doc/components/index.yaml | 9 ++ .../doc/components/parameter - Copy (2).yaml | 112 ------------------ .../doc/components/parameter - Copy.yaml | 112 ------------------ .../doc/components/parameter.yaml | 112 ------------------ .../doc/components/parameters.yaml | 8 ++ .../doc/components/schemas.yaml | 30 +++++ WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml | 48 ++------ 8 files changed, 62 insertions(+), 377 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln index 70b4eaae..58347b91 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -21,6 +21,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A doc\schemas\index.yaml = doc\schemas\index.yaml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670}" + ProjectSection(SolutionItems) = preProject + doc\components\index.yaml = doc\components\index.yaml + doc\components\parameters.yaml = doc\components\parameters.yaml + doc\components\schemas.yaml = doc\components\schemas.yaml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,5 +47,6 @@ Global {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + {4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670} = {7C405A84-132F-43F2-9F97-388CC40BED1D} EndGlobalSection EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml new file mode 100644 index 00000000..21c814fc --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml @@ -0,0 +1,9 @@ +parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml new file mode 100644 index 00000000..b8dcfa9a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml @@ -0,0 +1,8 @@ +petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml new file mode 100644 index 00000000..bf11a600 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml @@ -0,0 +1,30 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + +Pets: + type: array + items: + $ref: "#/Pet" + +Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml index 09458854..04dd77db 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml @@ -33,13 +33,14 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Pets" + $ref: "components/schemas.yaml#/Pets" + default: description: unexpected error content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: 'components/schemas.yaml#/Error' post: summary: Create a pet operationId: createPets @@ -53,7 +54,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: "components/schemas.yaml#/Error" /pets/{petId}: get: summary: Info for a specific pet @@ -61,52 +62,17 @@ paths: tags: - pets parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string + - $ref: "components/parameters.yaml#/petId" responses: '200': description: Expected response to a valid request content: application/json: schema: - $ref: "#/components/schemas/Pet" + $ref: "components/schemas.yaml#/Pet" default: description: unexpected error content: application/json: schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file + $ref: "components/schemas.yaml#/Error" \ No newline at end of file From a857da061ebe68b6091de6b69da10e3dcf6d5771 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 23:52:09 +0800 Subject: [PATCH 244/267] feat: merge file --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 13 ++++++------- WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 8 ++++++++ .../Lab.SpecFirst2/doc/schemas/index.yaml | 19 ------------------- 3 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln index 58347b91..37c434a3 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -16,18 +16,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Web", "src\La EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Adapter", "src\Lab.SpecFirst.Adapter\Lab.SpecFirst.Adapter.csproj", "{F5AAACC5-781F-41E6-8CD5-39389A00942C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A302F-7CB9-411A-A189-33919965A007}" - ProjectSection(SolutionItems) = preProject - doc\schemas\index.yaml = doc\schemas\index.yaml - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670}" ProjectSection(SolutionItems) = preProject - doc\components\index.yaml = doc\components\index.yaml doc\components\parameters.yaml = doc\components\parameters.yaml doc\components\schemas.yaml = doc\components\schemas.yaml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "merge", "merge", "{F28E0DEE-5774-42CF-ABE2-2654AF751215}" + ProjectSection(SolutionItems) = preProject + doc\merge\index.yaml = doc\merge\index.yaml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,7 +45,7 @@ Global GlobalSection(NestedProjects) = preSolution {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} - {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} {4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + {F28E0DEE-5774-42CF-ABE2-2654AF751215} = {7C405A84-132F-43F2-9F97-388CC40BED1D} EndGlobalSection EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml index 61cb3a6a..fe24c234 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -18,3 +18,11 @@ tasks: desc: 產生 Server Code cmds: - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson + + merge-swagger-file: + desc: 合併 Swagger File + cmds: +# - speccy resolve ./doc/index.yaml -o ./doc/merge/index.yaml +# - swagger-cli bundle ./doc/index.yaml --outfile ./doc/merge/index.yaml --type yaml + - openapi-merger -i ./doc/index.yaml -o ./doc/merge/index.yaml + \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml deleted file mode 100644 index 48e6371b..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml +++ /dev/null @@ -1,19 +0,0 @@ -parameters: - petId: - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - -schemas: - -requestBodies: -responses: - okResponse: - type: object - properties: - data: - example: ok - type: string From 65158ec807ff1954165da1c3bb9740ad8fd2cc67 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 20 Jul 2022 09:36:15 +0800 Subject: [PATCH 245/267] modify task --- WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 17 +-- .../Lab.SpecFirst2/doc/merge/index.yaml | 115 ++++++++++++++++++ 2 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml index fe24c234..ae1af0b1 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -3,23 +3,24 @@ version: "3" dotenv: [ "secrets/secrets.env" ] tasks: - rest-codegen-code: + spec-codegen: desc: 產生 Client / Server Code cmds: - - task: rest-codegen-client - - task: rest-codegen-server + - task: spec-merge-file + - task: spec-codegen-client + - task: spec-codegen-server - rest-codegen-client: + spec-codegen-client: desc: 產生 Client Code cmds: - - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false + - nswag openapi2csclient /input:doc/merge/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false - rest-codegen-server: + spec-codegen-server: desc: 產生 Server Code cmds: - - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson + - nswag openapi2cscontroller /input:doc/merge/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson - merge-swagger-file: + spec-merge-file: desc: 合併 Swagger File cmds: # - speccy resolve ./doc/index.yaml -o ./doc/merge/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml new file mode 100644 index 00000000..8b5b6d4d --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: 'http://localhost:7087/api/' +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Pets' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/pets/{petId}': + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - $ref: '#/components/parameters/petId' + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: '#/components/schemas/Pet' From 85f9f3b4ee9bdbe477e1c3aca006c83cef802ee7 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 20 Jul 2022 09:44:01 +0800 Subject: [PATCH 246/267] regen --- .../AutoGenerated/LabSpecClient.cs | 38 +++++++++---------- .../AutoGenerated/Controller.cs | 38 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs index 55ae054c..1aecf161 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs @@ -434,17 +434,14 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pet + public partial class Error { - [System.Text.Json.Serialization.JsonPropertyName("id")] - public long Id { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonPropertyName("message")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Name { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("tag")] - public string Tag { get; set; } + public string Message { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -459,20 +456,17 @@ public System.Collections.Generic.IDictionary AdditionalProperti } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pets : System.Collections.ObjectModel.Collection - { - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Error + public partial class Pet { - [System.Text.Json.Serialization.JsonPropertyName("code")] - public int Code { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.Text.Json.Serialization.JsonPropertyName("name")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Message { get; set; } + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -484,6 +478,12 @@ public System.Collections.Generic.IDictionary AdditionalProperti } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + } [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs index c458dd95..ddb19bf6 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -75,17 +75,14 @@ public System.Threading.Tasks.Task ShowPetById(string petId) } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pet + public partial class Error { - [System.Text.Json.Serialization.JsonPropertyName("id")] - public long Id { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonPropertyName("message")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Name { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("tag")] - public string Tag { get; set; } + public string Message { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -100,20 +97,17 @@ public System.Collections.Generic.IDictionary AdditionalProperti } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pets : System.Collections.ObjectModel.Collection - { - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Error + public partial class Pet { - [System.Text.Json.Serialization.JsonPropertyName("code")] - public int Code { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.Text.Json.Serialization.JsonPropertyName("name")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Message { get; set; } + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -126,6 +120,12 @@ public System.Collections.Generic.IDictionary AdditionalProperti } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } } From 1e35d1d40e2f2266b121b588c3a7381cb676be21 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 00:59:26 +0800 Subject: [PATCH 247/267] from fork lab.specfirst --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 44 ++ WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 20 + .../doc/components/parameter - Copy (2).yaml | 112 ++++ .../doc/components/parameter - Copy.yaml | 112 ++++ .../doc/components/parameter.yaml | 112 ++++ WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml | 112 ++++ .../Lab.SpecFirst2/doc/schemas/index.yaml | 19 + .../AutoGenerated/LabSpecClient.cs | 531 ++++++++++++++++++ .../Lab.SpecFirst.Adapter.csproj | 9 + .../src/Lab.SpecFirst.Web/.dockerignore | 25 + .../AutoGenerated/Controller.cs | 137 +++++ .../Controllers/SpecFirstController.cs | 38 ++ .../src/Lab.SpecFirst.Web/Dockerfile | 20 + .../Lab.SpecFirst.Web.csproj | 14 + .../src/Lab.SpecFirst.Web/Program.cs | 29 + .../Properties/launchSettings.json | 31 + .../src/Lab.SpecFirst.Web/WeatherForecast.cs | 12 + .../appsettings.Development.json | 8 + .../src/Lab.SpecFirst.Web/appsettings.json | 9 + 19 files changed, 1394 insertions(+) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln new file mode 100644 index 00000000..70b4eaae --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{1C8C1BD9-1338-47A0-963B-D35B1AD07476}" + ProjectSection(SolutionItems) = preProject + Taskfile.yml = Taskfile.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{7C405A84-132F-43F2-9F97-388CC40BED1D}" + ProjectSection(SolutionItems) = preProject + doc\index.yaml = doc\index.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F900DCE0-EF45-4629-AA24-949E65BAB714}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Web", "src\Lab.SpecFirst.Web\Lab.SpecFirst.Web.csproj", "{70299524-F9F4-41DF-80EF-D1CE03C2965A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Adapter", "src\Lab.SpecFirst.Adapter\Lab.SpecFirst.Adapter.csproj", "{F5AAACC5-781F-41E6-8CD5-39389A00942C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A302F-7CB9-411A-A189-33919965A007}" + ProjectSection(SolutionItems) = preProject + doc\schemas\index.yaml = doc\schemas\index.yaml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70299524-F9F4-41DF-80EF-D1CE03C2965A}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AAACC5-781F-41E6-8CD5-39389A00942C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} + {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + EndGlobalSection +EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml new file mode 100644 index 00000000..61cb3a6a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -0,0 +1,20 @@ +version: "3" + +dotenv: [ "secrets/secrets.env" ] + +tasks: + rest-codegen-code: + desc: 產生 Client / Server Code + cmds: + - task: rest-codegen-client + - task: rest-codegen-server + + rest-codegen-client: + desc: 產生 Client Code + cmds: + - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false + + rest-codegen-server: + desc: 產生 Server Code + cmds: + - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml new file mode 100644 index 00000000..09458854 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml @@ -0,0 +1,112 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://localhost:7087/api/ +# - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml new file mode 100644 index 00000000..48e6371b --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml @@ -0,0 +1,19 @@ +parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + +schemas: + +requestBodies: +responses: + okResponse: + type: object + properties: + data: + example: ok + type: string diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs new file mode 100644 index 00000000..55ae054c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs @@ -0,0 +1,531 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Adapter +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial interface ILabSpecClient + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken); + + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class LabSpecClient : ILabSpecClient + { + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public LabSpecClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public System.Threading.Tasks.Task> ListPetsAsync(int? limit) + { + return ListPetsAsync(limit, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + /// A server side error occurred. + public async System.Threading.Tasks.Task> ListPetsAsync(int? limit, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets?"); + if (limit != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("limit") + "=").Append(System.Uri.EscapeDataString(ConvertToString(limit, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create a pet + /// Null response + /// A server side error occurred. + public System.Threading.Tasks.Task CreatePetsAsync() + { + return CreatePetsAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create a pet + /// Null response + /// A server side error occurred. + public async System.Threading.Tasks.Task CreatePetsAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public System.Threading.Tasks.Task ShowPetByIdAsync(string petId) + { + return ShowPetByIdAsync(petId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + /// A server side error occurred. + public async System.Threading.Tasks.Task ShowPetByIdAsync(string petId, System.Threading.CancellationToken cancellationToken) + { + if (petId == null) + throw new System.ArgumentNullException("petId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append("pets/{petId}"); + urlBuilder_.Replace("{petId}", System.Uri.EscapeDataString(ConvertToString(petId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("unexpected error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj new file mode 100644 index 00000000..eb2460e9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/Lab.SpecFirst.Adapter.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore new file mode 100644 index 00000000..cd967fc3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs new file mode 100644 index 00000000..c458dd95 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -0,0 +1,137 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" + +namespace Lab.SpecFirst.Web.Controllers +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + public interface ISpecFirstContractController + { + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + System.Threading.Tasks.Task> ListPetsAsync(int? limit); + + /// Create a pet + /// Null response + System.Threading.Tasks.Task CreatePetsAsync(); + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + System.Threading.Tasks.Task ShowPetByIdAsync(string petId); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] + [Microsoft.AspNetCore.Mvc.Route("api/")] + public partial class SpecFirstContractController : Microsoft.AspNetCore.Mvc.ControllerBase + { + private ISpecFirstContractController _implementation; + + public SpecFirstContractController(ISpecFirstContractController implementation) + { + _implementation = implementation; + } + + /// List all pets + /// How many items to return at one time (max 100) + /// A paged array of pets + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task> ListPets([Microsoft.AspNetCore.Mvc.FromQuery] int? limit) + { + return _implementation.ListPetsAsync(limit); + } + + /// Create a pet + /// Null response + [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("pets")] + public System.Threading.Tasks.Task CreatePets() + { + return _implementation.CreatePetsAsync(); + } + + /// Info for a specific pet + /// The id of the pet to retrieve + /// Expected response to a valid request + [Microsoft.AspNetCore.Mvc.HttpGet, Microsoft.AspNetCore.Mvc.Route("pets/{petId}")] + public System.Threading.Tasks.Task ShowPetById(string petId) + { + return _implementation.ShowPetByIdAsync(petId); + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pet + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Error + { + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 +#pragma warning restore 3016 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs new file mode 100644 index 00000000..eed7b89a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Controllers/SpecFirstController.cs @@ -0,0 +1,38 @@ +namespace Lab.SpecFirst.Web.Controllers; + +class SpecFirstController : ISpecFirstContractController +{ + public async Task> ListPetsAsync(int? limit) + { + return new List() + { + new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }, + }; + } + + public async Task CreatePetsAsync() + { + + } + + public async Task ShowPetByIdAsync(string petId) + { + return new() + { + Id = 1, + Name = "yao", + Tag = "dog", + AdditionalProperties = new Dictionary() + { + } + }; + } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile new file mode 100644 index 00000000..e568de25 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj", "Lab.SpecFirst.Web/"] +RUN dotnet restore "src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj" +COPY . . +WORKDIR "/src/Lab.SpecFirst.Web" +RUN dotnet build "Lab.SpecFirst.Web.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Lab.SpecFirst.Web.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Lab.SpecFirst.Web.dll"] diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj new file mode 100644 index 00000000..0db9dec4 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Lab.SpecFirst.Web.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs new file mode 100644 index 00000000..8f8bd1ae --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Program.cs @@ -0,0 +1,29 @@ +using Lab.SpecFirst.Web.Controllers; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddScoped(); + +// Add services to the container. +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json new file mode 100644 index 00000000..466bd7be --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:9860", + "sslPort": 44313 + } + }, + "profiles": { + "Lab.SpecFirst.Web": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7041;http://localhost:5041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs new file mode 100644 index 00000000..bacb51a3 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Lab.SpecFirst.Web; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From df670f19a793fc26b5ee521680cc49133e14273f Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 10:02:56 +0800 Subject: [PATCH 248/267] feat: extra schema module --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 8 ++ .../Lab.SpecFirst2/doc/components/index.yaml | 9 ++ .../doc/components/parameter - Copy (2).yaml | 112 ------------------ .../doc/components/parameter - Copy.yaml | 112 ------------------ .../doc/components/parameter.yaml | 112 ------------------ .../doc/components/parameters.yaml | 8 ++ .../doc/components/schemas.yaml | 30 +++++ WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml | 48 ++------ 8 files changed, 62 insertions(+), 377 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln index 70b4eaae..58347b91 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -21,6 +21,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A doc\schemas\index.yaml = doc\schemas\index.yaml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670}" + ProjectSection(SolutionItems) = preProject + doc\components\index.yaml = doc\components\index.yaml + doc\components\parameters.yaml = doc\components\parameters.yaml + doc\components\schemas.yaml = doc\components\schemas.yaml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,5 +47,6 @@ Global {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + {4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670} = {7C405A84-132F-43F2-9F97-388CC40BED1D} EndGlobalSection EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml new file mode 100644 index 00000000..21c814fc --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/index.yaml @@ -0,0 +1,9 @@ +parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy (2).yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter - Copy.yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml deleted file mode 100644 index 09458854..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameter.yaml +++ /dev/null @@ -1,112 +0,0 @@ -openapi: "3.0.3" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://localhost:7087/api/ -# - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pet" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml new file mode 100644 index 00000000..b8dcfa9a --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/parameters.yaml @@ -0,0 +1,8 @@ +petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml new file mode 100644 index 00000000..bf11a600 --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/components/schemas.yaml @@ -0,0 +1,30 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + +Pets: + type: array + items: + $ref: "#/Pet" + +Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml index 09458854..04dd77db 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/index.yaml @@ -33,13 +33,14 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Pets" + $ref: "components/schemas.yaml#/Pets" + default: description: unexpected error content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: 'components/schemas.yaml#/Error' post: summary: Create a pet operationId: createPets @@ -53,7 +54,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: "components/schemas.yaml#/Error" /pets/{petId}: get: summary: Info for a specific pet @@ -61,52 +62,17 @@ paths: tags: - pets parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string + - $ref: "components/parameters.yaml#/petId" responses: '200': description: Expected response to a valid request content: application/json: schema: - $ref: "#/components/schemas/Pet" + $ref: "components/schemas.yaml#/Pet" default: description: unexpected error content: application/json: schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file + $ref: "components/schemas.yaml#/Error" \ No newline at end of file From 5396074d9639719bafb8cc5dde846facc17862f2 Mon Sep 17 00:00:00 2001 From: yao Date: Tue, 19 Jul 2022 23:52:09 +0800 Subject: [PATCH 249/267] feat: merge file --- .../Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln | 13 ++++++------- WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 8 ++++++++ .../Lab.SpecFirst2/doc/schemas/index.yaml | 19 ------------------- 3 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln index 58347b91..37c434a3 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln +++ b/WebAPI/Swagger/Lab.SpecFirst2/Lab.SpecFirst.sln @@ -16,18 +16,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Web", "src\La EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpecFirst.Adapter", "src\Lab.SpecFirst.Adapter\Lab.SpecFirst.Adapter.csproj", "{F5AAACC5-781F-41E6-8CD5-39389A00942C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schemas", "schemas", "{087A302F-7CB9-411A-A189-33919965A007}" - ProjectSection(SolutionItems) = preProject - doc\schemas\index.yaml = doc\schemas\index.yaml - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670}" ProjectSection(SolutionItems) = preProject - doc\components\index.yaml = doc\components\index.yaml doc\components\parameters.yaml = doc\components\parameters.yaml doc\components\schemas.yaml = doc\components\schemas.yaml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "merge", "merge", "{F28E0DEE-5774-42CF-ABE2-2654AF751215}" + ProjectSection(SolutionItems) = preProject + doc\merge\index.yaml = doc\merge\index.yaml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,7 +45,7 @@ Global GlobalSection(NestedProjects) = preSolution {70299524-F9F4-41DF-80EF-D1CE03C2965A} = {F900DCE0-EF45-4629-AA24-949E65BAB714} {F5AAACC5-781F-41E6-8CD5-39389A00942C} = {F900DCE0-EF45-4629-AA24-949E65BAB714} - {087A302F-7CB9-411A-A189-33919965A007} = {7C405A84-132F-43F2-9F97-388CC40BED1D} {4D3DDFBF-E6C5-4E38-8FC2-AE1C10450670} = {7C405A84-132F-43F2-9F97-388CC40BED1D} + {F28E0DEE-5774-42CF-ABE2-2654AF751215} = {7C405A84-132F-43F2-9F97-388CC40BED1D} EndGlobalSection EndGlobal diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml index 61cb3a6a..fe24c234 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -18,3 +18,11 @@ tasks: desc: 產生 Server Code cmds: - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson + + merge-swagger-file: + desc: 合併 Swagger File + cmds: +# - speccy resolve ./doc/index.yaml -o ./doc/merge/index.yaml +# - swagger-cli bundle ./doc/index.yaml --outfile ./doc/merge/index.yaml --type yaml + - openapi-merger -i ./doc/index.yaml -o ./doc/merge/index.yaml + \ No newline at end of file diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml deleted file mode 100644 index 48e6371b..00000000 --- a/WebAPI/Swagger/Lab.SpecFirst2/doc/schemas/index.yaml +++ /dev/null @@ -1,19 +0,0 @@ -parameters: - petId: - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - -schemas: - -requestBodies: -responses: - okResponse: - type: object - properties: - data: - example: ok - type: string From be5121c9d7557c900ed86c797d571313dbb2072a Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 20 Jul 2022 09:36:15 +0800 Subject: [PATCH 250/267] modify task --- WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml | 17 +-- .../Lab.SpecFirst2/doc/merge/index.yaml | 115 ++++++++++++++++++ 2 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml index fe24c234..ae1af0b1 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml +++ b/WebAPI/Swagger/Lab.SpecFirst2/Taskfile.yml @@ -3,23 +3,24 @@ version: "3" dotenv: [ "secrets/secrets.env" ] tasks: - rest-codegen-code: + spec-codegen: desc: 產生 Client / Server Code cmds: - - task: rest-codegen-client - - task: rest-codegen-server + - task: spec-merge-file + - task: spec-codegen-client + - task: spec-codegen-server - rest-codegen-client: + spec-codegen-client: desc: 產生 Client Code cmds: - - nswag openapi2csclient /input:doc/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false + - nswag openapi2csclient /input:doc/merge/index.yaml /classname:LabSpecClient /namespace:Lab.SpecFirst.Adapter /output:src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs /jsonLibrary:SystemTextJson /generateClientInterfaces:true /exposeJsonSerializerSettings:false /useBaseUrl:false - rest-codegen-server: + spec-codegen-server: desc: 產生 Server Code cmds: - - nswag openapi2cscontroller /input:doc/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson + - nswag openapi2cscontroller /input:doc/merge/index.yaml /classname:SpecFirstContract /namespace:Lab.SpecFirst.Web.Controllers /output:src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs /jsonLibrary:SystemTextJson - merge-swagger-file: + spec-merge-file: desc: 合併 Swagger File cmds: # - speccy resolve ./doc/index.yaml -o ./doc/merge/index.yaml diff --git a/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml b/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml new file mode 100644 index 00000000..8b5b6d4d --- /dev/null +++ b/WebAPI/Swagger/Lab.SpecFirst2/doc/merge/index.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: 'http://localhost:7087/api/' +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Pets' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/pets/{petId}': + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - $ref: '#/components/parameters/petId' + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + parameters: + petId: + name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + example: 1 + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: '#/components/schemas/Pet' From bcb1a92edfaee14efd6a9952467fe8391848e83c Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 20 Jul 2022 09:44:01 +0800 Subject: [PATCH 251/267] regen --- .../AutoGenerated/LabSpecClient.cs | 38 +++++++++---------- .../AutoGenerated/Controller.cs | 38 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs index 55ae054c..1aecf161 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Adapter/AutoGenerated/LabSpecClient.cs @@ -434,17 +434,14 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pet + public partial class Error { - [System.Text.Json.Serialization.JsonPropertyName("id")] - public long Id { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonPropertyName("message")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Name { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("tag")] - public string Tag { get; set; } + public string Message { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -459,20 +456,17 @@ public System.Collections.Generic.IDictionary AdditionalProperti } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pets : System.Collections.ObjectModel.Collection - { - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Error + public partial class Pet { - [System.Text.Json.Serialization.JsonPropertyName("code")] - public int Code { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.Text.Json.Serialization.JsonPropertyName("name")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Message { get; set; } + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -484,6 +478,12 @@ public System.Collections.Generic.IDictionary AdditionalProperti } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + } [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")] diff --git a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs index c458dd95..ddb19bf6 100644 --- a/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs +++ b/WebAPI/Swagger/Lab.SpecFirst2/src/Lab.SpecFirst.Web/AutoGenerated/Controller.cs @@ -75,17 +75,14 @@ public System.Threading.Tasks.Task ShowPetById(string petId) } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pet + public partial class Error { - [System.Text.Json.Serialization.JsonPropertyName("id")] - public long Id { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("code")] + public int Code { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonPropertyName("message")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Name { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("tag")] - public string Tag { get; set; } + public string Message { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -100,20 +97,17 @@ public System.Collections.Generic.IDictionary AdditionalProperti } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Pets : System.Collections.ObjectModel.Collection - { - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] - public partial class Error + public partial class Pet { - [System.Text.Json.Serialization.JsonPropertyName("code")] - public int Code { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("message")] + [System.Text.Json.Serialization.JsonPropertyName("name")] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] - public string Message { get; set; } + public string Name { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("tag")] + public string Tag { get; set; } private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); @@ -126,6 +120,12 @@ public System.Collections.Generic.IDictionary AdditionalProperti } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class Pets : System.Collections.ObjectModel.Collection + { + + } } From f2c4780b89cd13d74956006f71ef446fc6ab9a64 Mon Sep 17 00:00:00 2001 From: yao Date: Wed, 17 Aug 2022 00:06:00 +0800 Subject: [PATCH 252/267] =?UTF-8?q?feat:=20=E7=AF=84=E4=BE=8B=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs | 61 +++++++ .../Mock Server/Prism/doc/1/index.yaml | 111 ++++++++++++ .../Mock Server/Prism/doc/2/index.yaml | 169 ++++++++++++++++++ .../Mock Server/Prism/src/Lab.MockServer.sln | 26 +++ 4 files changed, 367 insertions(+) create mode 100644 WebAPI/Swagger/Mock Server/Prism/doc/1/index.yaml create mode 100644 WebAPI/Swagger/Mock Server/Prism/doc/2/index.yaml create mode 100644 WebAPI/Swagger/Mock Server/Prism/src/Lab.MockServer.sln diff --git a/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs index 6a096e8b..1e2f17d0 100644 --- a/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs +++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs @@ -1,12 +1,73 @@ +using System.Collections.Concurrent; +using System.Reflection; using Amazon; using Amazon.S3; using Amazon.S3.Model; namespace Lab.Aws.S3.MinIOS3; +public class FieldTypeAssistant +{ + private static ConcurrentDictionary> s_fieldTypeList = new(); + + public static Dictionary GetStaticFieldValues() + { + var type = typeof(T); + var fieldTypeList = s_fieldTypeList; + if (fieldTypeList.TryGetValue(type, out var results)) + { + return results; + } + + var bindingFlags = BindingFlags.Public + | BindingFlags.Static + ; + results = new Dictionary(); + var fieldInfosInfos = type.GetFields(bindingFlags); + foreach (var fieldInfo in fieldInfosInfos) + { + var key = fieldInfo.Name; + var value = fieldInfo.GetValue(null); + + results.Add(value.ToString(), key); + } + + fieldTypeList.TryAdd(type, results); + return results; + } +} + +public class ProfileFieldNames +{ + public const string BB1Name = "BB1"; + + public const string BB2Name = "BB2"; + + private static readonly Lazy> s_valueDictionary = + new(FieldTypeAssistant.GetStaticFieldValues()); + + public static IReadOnlyDictionary GetValues() + { + return s_valueDictionary.Value; + } + + public static string GetValue(string key) + { + s_valueDictionary.Value.TryGetValue(key, out var value); + return value; + } +} + [TestClass] public class UnitTest1 { + [TestMethod] + public void test() + { + var actual = ProfileFieldNames.GetValue("BB1"); + Assert.AreEqual("BB1Name", actual); + } + [TestMethod] public async Task 新增一個儲存桶() { diff --git a/WebAPI/Swagger/Mock Server/Prism/doc/1/index.yaml b/WebAPI/Swagger/Mock Server/Prism/doc/1/index.yaml new file mode 100644 index 00000000..acdeb1fd --- /dev/null +++ b/WebAPI/Swagger/Mock Server/Prism/doc/1/index.yaml @@ -0,0 +1,111 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Mock Server/Prism/doc/2/index.yaml b/WebAPI/Swagger/Mock Server/Prism/doc/2/index.yaml new file mode 100644 index 00000000..79724a80 --- /dev/null +++ b/WebAPI/Swagger/Mock Server/Prism/doc/2/index.yaml @@ -0,0 +1,169 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + example: + - id: 10 + name: "doggie" + tag: "dog" + + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + examples: + 1: + value: + id: 1 + name: "doggie" + tag: "dog" + 12: + value: + id: 12 + name: "dora" + tag: "cat" + '404': + description: Pet not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + message: "Pet not found" + status: 404 + code: 404 + + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/validate: + get: + summary: Your GET endpoint + tags: [] + parameters: + - schema: + type: string + pattern: '^[A-Z]{2} [1-9]{4}$' + minLength: 0 + in: query + name: id + required: true + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + examples: + 1: + value: + id: 1 + name: "doggie" + tag: "dog" + 12: + value: + id: 12 + name: "dora" + tag: "cat" + +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/WebAPI/Swagger/Mock Server/Prism/src/Lab.MockServer.sln b/WebAPI/Swagger/Mock Server/Prism/src/Lab.MockServer.sln new file mode 100644 index 00000000..3cf1d2a9 --- /dev/null +++ b/WebAPI/Swagger/Mock Server/Prism/src/Lab.MockServer.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{3B9B5D72-1C50-43D1-8E33-FBE93C4D0FF8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1", "1", "{DA17785E-5C85-4A31-AFA6-B5BF799B3DDD}" + ProjectSection(SolutionItems) = preProject + ..\doc\1\index.yaml = ..\doc\1\index.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2", "2", "{F7194878-92F6-4FE5-9841-DE99711949F6}" + ProjectSection(SolutionItems) = preProject + ..\doc\2\index.yaml = ..\doc\2\index.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{A84DAC1D-5160-47E1-916B-892AB78EA2E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DA17785E-5C85-4A31-AFA6-B5BF799B3DDD} = {3B9B5D72-1C50-43D1-8E33-FBE93C4D0FF8} + {F7194878-92F6-4FE5-9841-DE99711949F6} = {3B9B5D72-1C50-43D1-8E33-FBE93C4D0FF8} + EndGlobalSection +EndGlobal From 2070c3ad8b61eb7f18520ec81254fc9c841dd676 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 20 Aug 2022 16:23:06 +0800 Subject: [PATCH 253/267] feat: add test --- ...iddleware.OverrideResponse.UnitTest.csproj | 23 ++++ ...errideResponseHandlerMiddlewareUnitTest.cs | 103 ++++++++++++++++++ .../Usings.cs | 1 + .../Lib.Middleware.OverrideResponse.sln | 22 ++++ .../Lib.Middleware.OverrideResponse.csproj | 13 +++ .../OverrideResponseHandlerMiddleware.cs | 59 ++++++++++ 6 files changed, 221 insertions(+) create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Usings.cs create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.sln create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj create mode 100644 AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj new file mode 100644 index 00000000..288baefd --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + + + + + + diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs new file mode 100644 index 00000000..4257f7f0 --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs @@ -0,0 +1,103 @@ +using System.ComponentModel; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Unicode; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Lib.Middleware.OverrideResponse.UnitTest; + +[TestClass] +public class OverrideResponseHandlerMiddlewareUnitTest +{ + [TestMethod] + public async Task 不模糊內部訊息() + { + var expected = @"{""code"":""9527""}"; + + var serviceProvider = CreateServiceProvider(); + var jsonSerializerOptions = serviceProvider.GetService(); + var logger = serviceProvider.GetService>(); + var target = new OverrideResponseHandlerMiddleware(nextContext => + CreateFakeNextContext(nextContext, new { Code = "9527" }, StatusCodes.Status200OK)); + + var httpContext = new DefaultHttpContext + { + Response = { Body = new MemoryStream() } + }; + + await target.InvokeAsync(httpContext, logger, jsonSerializerOptions); + var response = httpContext.Response; + var stream = response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public async Task 模糊化未驗證訊息() + { + var expected = @"{""errorCode"":""NoAuthentication"",""errorMessage"":""Please contact your administrator""}"; + var httpContext = new DefaultHttpContext + { + Response = { Body = new MemoryStream() } + }; + var serviceProvider = CreateServiceProvider(); + var jsonSerializerOptions = serviceProvider.GetService(); + var logger = serviceProvider.GetService>(); + + var target = new OverrideResponseHandlerMiddleware(nextContext => + CreateFakeNextContext(nextContext, new + { + ErrorCode = "NoAuthentication", + ErrorMessage = "Invalid userid or password" + }, StatusCodes.Status401Unauthorized)); + + await target.InvokeAsync(httpContext, logger, jsonSerializerOptions); + + var response = httpContext.Response; + var stream = response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.AreEqual(expected, actual); + } + + private static Task CreateFakeNextContext(HttpContext context, object detailFailure, int statusCode) + { + context.Response.StatusCode = statusCode; + context.Response.WriteAsJsonAsync(detailFailure); + return Task.CompletedTask; + } + + private static JsonSerializerOptions CreateJsonSerializerOptions() + { + return new() + { + Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, + UnicodeRanges.CjkUnifiedIdeographs), + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + } + + private static IServiceProvider CreateServiceProvider() + { + var services = new ServiceCollection(); + services.AddSingleton(p => CreateJsonSerializerOptions()); + services.AddSingleton(p => LoggerFactory.Create(builder => { builder.AddConsole(); })); + services.AddSingleton(p => p.GetService().CreateLogger()); + return services.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Usings.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Usings.cs new file mode 100644 index 00000000..ab67c7ea --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Usings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.sln b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.sln new file mode 100644 index 00000000..8868720c --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.Middleware.OverrideResponse", "Lib.Middleware.OverrideResponse\Lib.Middleware.OverrideResponse.csproj", "{F696FEA1-4126-42F0-8D2F-6F7BE99DF418}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.Middleware.OverrideResponse.UnitTest", "Lib.Middleware.OverrideResponse.UnitTest\Lib.Middleware.OverrideResponse.UnitTest.csproj", "{F5FD889F-037A-476E-B1F2-A01769A34674}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F696FEA1-4126-42F0-8D2F-6F7BE99DF418}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F696FEA1-4126-42F0-8D2F-6F7BE99DF418}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F696FEA1-4126-42F0-8D2F-6F7BE99DF418}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F696FEA1-4126-42F0-8D2F-6F7BE99DF418}.Release|Any CPU.Build.0 = Release|Any CPU + {F5FD889F-037A-476E-B1F2-A01769A34674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5FD889F-037A-476E-B1F2-A01769A34674}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5FD889F-037A-476E-B1F2-A01769A34674}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5FD889F-037A-476E-B1F2-A01769A34674}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj new file mode 100644 index 00000000..784167a0 --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs new file mode 100644 index 00000000..4007c220 --- /dev/null +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs @@ -0,0 +1,59 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Lib.Middleware.OverrideResponse; + +public class OverrideResponseHandlerMiddleware +{ + private readonly RequestDelegate _next; + + public OverrideResponseHandlerMiddleware(RequestDelegate next) + { + this._next = next; + } + + public async Task InvokeAsync(HttpContext context, + ILogger logger, + JsonSerializerOptions jsonSerializerOptions) + { + var srcStream = context.Response.Body; + await using var destStream = new MemoryStream(); + context.Response.Body = destStream; + + await this._next(context); + + destStream.Seek(0, SeekOrigin.Begin); + + var realResponseBody = await new StreamReader(destStream).ReadToEndAsync(); + destStream.Seek(0, SeekOrigin.Begin); + // await destStream.CopyToAsync(srcStream); + context.Response.Body = srcStream; + + var fuzzyBody = context.Response.StatusCode switch + { + 401 => CreateFuzzyBody("NoAuthentication"), + 403 => CreateFuzzyBody("NoAuthorization"), + _ => null + }; + + if (fuzzyBody != null) + { + var json = JsonSerializer.Serialize(fuzzyBody, jsonSerializerOptions); + await context.Response.WriteAsync(json); + } + else + { + await context.Response.WriteAsync(realResponseBody); + } + } + + private static object CreateFuzzyBody(string failureCode) + { + return new + { + ErrorCode = failureCode, + ErrorMessage = "Please contact your administrator" + }; + } +} \ No newline at end of file From 3249bc1911dfdef17d9d7abeb91747d313b581b3 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 20 Aug 2022 16:54:02 +0800 Subject: [PATCH 254/267] refactor: add test case --- ...errideResponseHandlerMiddlewareUnitTest.cs | 32 +++++++++++++++++++ .../OverrideResponseHandlerMiddleware.cs | 24 +++++++------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs index 4257f7f0..fe05535b 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs @@ -72,6 +72,38 @@ public async Task 模糊化未驗證訊息() Assert.AreEqual(expected, actual); } + [TestMethod] + public async Task 模糊化未授權訊息() + { + var expected = @"{""errorCode"":""NoAuthorization"",""errorMessage"":""Please contact your administrator""}"; + var httpContext = new DefaultHttpContext + { + Response = { Body = new MemoryStream() } + }; + var serviceProvider = CreateServiceProvider(); + var jsonSerializerOptions = serviceProvider.GetService(); + var logger = serviceProvider.GetService>(); + + var target = new OverrideResponseHandlerMiddleware(nextContext => + CreateFakeNextContext(nextContext, new + { + ErrorCode = "NoAuthorization", + ErrorMessage = "No permission" + }, StatusCodes.Status403Forbidden)); + + await target.InvokeAsync(httpContext, logger, jsonSerializerOptions); + + var response = httpContext.Response; + var stream = response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var actual = await new StreamReader(stream).ReadToEndAsync(); + Assert.AreEqual(expected, actual); + } + private static Task CreateFakeNextContext(HttpContext context, object detailFailure, int statusCode) { context.Response.StatusCode = statusCode; diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs index 4007c220..71c80a7f 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Net; +using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -17,20 +18,15 @@ public async Task InvokeAsync(HttpContext context, ILogger logger, JsonSerializerOptions jsonSerializerOptions) { - var srcStream = context.Response.Body; - await using var destStream = new MemoryStream(); - context.Response.Body = destStream; + var originalResponseBodyStream = context.Response.Body; + await using var newResponseBodyStream = new MemoryStream(); + context.Response.Body = newResponseBodyStream; await this._next(context); - destStream.Seek(0, SeekOrigin.Begin); - - var realResponseBody = await new StreamReader(destStream).ReadToEndAsync(); - destStream.Seek(0, SeekOrigin.Begin); - // await destStream.CopyToAsync(srcStream); - context.Response.Body = srcStream; - - var fuzzyBody = context.Response.StatusCode switch + newResponseBodyStream.Seek(0, SeekOrigin.Begin); + var statusCode = context.Response.StatusCode; + var fuzzyBody = statusCode switch { 401 => CreateFuzzyBody("NoAuthentication"), 403 => CreateFuzzyBody("NoAuthorization"), @@ -40,11 +36,13 @@ public async Task InvokeAsync(HttpContext context, if (fuzzyBody != null) { var json = JsonSerializer.Serialize(fuzzyBody, jsonSerializerOptions); + context.Response.Body = originalResponseBodyStream; await context.Response.WriteAsync(json); } else { - await context.Response.WriteAsync(realResponseBody); + await newResponseBodyStream.CopyToAsync(originalResponseBodyStream); + context.Response.Body = originalResponseBodyStream; } } From 5818b9e797bba6748b0ec418dc9ce935cc6acf40 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 20 Aug 2022 17:06:56 +0800 Subject: [PATCH 255/267] refactor --- .../OverrideResponseHandlerMiddleware.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs index 71c80a7f..9d26a89e 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs @@ -35,9 +35,14 @@ public async Task InvokeAsync(HttpContext context, if (fuzzyBody != null) { - var json = JsonSerializer.Serialize(fuzzyBody, jsonSerializerOptions); + var fuzzyData = JsonSerializer.Serialize(fuzzyBody, jsonSerializerOptions); + logger.LogInformation("Fuzzy data:{FuzzyData}", fuzzyData); + + var realData = await new StreamReader(newResponseBodyStream).ReadToEndAsync(); + logger.LogInformation("Read data:{RealData}", realData); + context.Response.Body = originalResponseBodyStream; - await context.Response.WriteAsync(json); + await context.Response.WriteAsync(fuzzyData); } else { From ab9d435ced406f683aa6ea5d1182cd2d95e07823 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 20 Aug 2022 17:15:48 +0800 Subject: [PATCH 256/267] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E5=A5=97=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Lib.Middleware.OverrideResponse.csproj | 6 +++++- .../OverrideResponseHandlerMiddleware.cs | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj index 784167a0..061f9081 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj @@ -7,7 +7,11 @@ - + + + + + diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs index 9d26a89e..22de1636 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs @@ -1,5 +1,4 @@ -using System.Net; -using System.Text.Json; +using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; From 840b4c5df43df03f14f39d5b5747e3ca91612a75 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 20 Aug 2022 17:44:48 +0800 Subject: [PATCH 257/267] refactor --- ...iddleware.OverrideResponse.UnitTest.csproj | 12 ++++----- ...errideResponseHandlerMiddlewareUnitTest.cs | 25 +++++++++---------- .../Lib.Middleware.OverrideResponse.csproj | 8 +++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj index 288baefd..b08e38ae 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/Lib.Middleware.OverrideResponse.UnitTest.csproj @@ -9,15 +9,15 @@ - - - - - + + + + + - + diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs index fe05535b..9527bcc9 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs @@ -1,4 +1,3 @@ -using System.ComponentModel; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; @@ -13,7 +12,7 @@ namespace Lib.Middleware.OverrideResponse.UnitTest; public class OverrideResponseHandlerMiddlewareUnitTest { [TestMethod] - public async Task 不模糊內部訊息() + public async Task 不模糊訊息() { var expected = @"{""code"":""9527""}"; @@ -41,9 +40,9 @@ public async Task 不模糊內部訊息() } [TestMethod] - public async Task 模糊化未驗證訊息() + public async Task 模糊化未授權訊息() { - var expected = @"{""errorCode"":""NoAuthentication"",""errorMessage"":""Please contact your administrator""}"; + var expected = @"{""errorCode"":""NoAuthorization"",""errorMessage"":""Please contact your administrator""}"; var httpContext = new DefaultHttpContext { Response = { Body = new MemoryStream() } @@ -55,9 +54,9 @@ public async Task 模糊化未驗證訊息() var target = new OverrideResponseHandlerMiddleware(nextContext => CreateFakeNextContext(nextContext, new { - ErrorCode = "NoAuthentication", - ErrorMessage = "Invalid userid or password" - }, StatusCodes.Status401Unauthorized)); + ErrorCode = "NoAuthorization", + ErrorMessage = "No permission" + }, StatusCodes.Status403Forbidden)); await target.InvokeAsync(httpContext, logger, jsonSerializerOptions); @@ -73,9 +72,9 @@ public async Task 模糊化未驗證訊息() } [TestMethod] - public async Task 模糊化未授權訊息() + public async Task 模糊化未驗證訊息() { - var expected = @"{""errorCode"":""NoAuthorization"",""errorMessage"":""Please contact your administrator""}"; + var expected = @"{""errorCode"":""NoAuthentication"",""errorMessage"":""Please contact your administrator""}"; var httpContext = new DefaultHttpContext { Response = { Body = new MemoryStream() } @@ -87,9 +86,9 @@ public async Task 模糊化未授權訊息() var target = new OverrideResponseHandlerMiddleware(nextContext => CreateFakeNextContext(nextContext, new { - ErrorCode = "NoAuthorization", - ErrorMessage = "No permission" - }, StatusCodes.Status403Forbidden)); + ErrorCode = "NoAuthentication", + ErrorMessage = "Invalid userid or password" + }, StatusCodes.Status401Unauthorized)); await target.InvokeAsync(httpContext, logger, jsonSerializerOptions); @@ -113,7 +112,7 @@ private static Task CreateFakeNextContext(HttpContext context, object detailFail private static JsonSerializerOptions CreateJsonSerializerOptions() { - return new() + return new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs), diff --git a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj index 061f9081..b6df6955 100644 --- a/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj +++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj @@ -7,10 +7,10 @@ - - - - + + + + From 2fdf8d54b1592a97022d49f959c2d20ac6c9f7f9 Mon Sep 17 00:00:00 2001 From: yao Date: Fri, 26 Aug 2022 13:58:07 +0800 Subject: [PATCH 258/267] add project --- .../Lab.Host.Env.ConsoleApp.csproj | 10 +++++++ .../src/Lab.Host.Env.ConsoleApp/Program.cs | 3 ++ .../Controllers/DemoController.cs | 28 +++++++++++++++++++ .../Lab.Host.Env.WebApi.csproj | 13 +++++++++ .../src/Lab.Host.Env.WebApi/Program.cs | 26 +++++++++++++++++ .../ServiceModels/EnvironmentResponse.cs | 5 ++++ .../appsettings.Development.json | 8 ++++++ .../src/Lab.Host.Env.WebApi/appsettings.json | 9 ++++++ Host/Lab.Host.Env/src/Lab.Host.Env.sln | 22 +++++++++++++++ 9 files changed, 124 insertions(+) create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Controllers/DemoController.cs create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Lab.Host.Env.WebApi.csproj create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/ServiceModels/EnvironmentResponse.cs create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.sln diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj new file mode 100644 index 00000000..b9de0634 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs new file mode 100644 index 00000000..e5dff12b --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Controllers/DemoController.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Controllers/DemoController.cs new file mode 100644 index 00000000..6449d88b --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Controllers/DemoController.cs @@ -0,0 +1,28 @@ +using Lab.Host.Env.WebApi.ServiceModels; +using Microsoft.AspNetCore.Mvc; + +namespace Lab.Host.Env.WebApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class DemoController : ControllerBase +{ + private IWebHostEnvironment _host; + private readonly ILogger _logger; + + public DemoController(ILogger logger, IWebHostEnvironment host) + { + this._logger = logger; + this._host = host; + } + + [HttpGet] + public async Task> Get(CancellationToken cancel = default) + { + return this.Ok(new + { + this._host.ApplicationName, + this._host.EnvironmentName + }); + } +} \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Lab.Host.Env.WebApi.csproj b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Lab.Host.Env.WebApi.csproj new file mode 100644 index 00000000..b9baca3e --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Lab.Host.Env.WebApi.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs new file mode 100644 index 00000000..329fe361 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs @@ -0,0 +1,26 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/ServiceModels/EnvironmentResponse.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/ServiceModels/EnvironmentResponse.cs new file mode 100644 index 00000000..3c9d2e75 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/ServiceModels/EnvironmentResponse.cs @@ -0,0 +1,5 @@ +namespace Lab.Host.Env.WebApi.ServiceModels; + +public class EnvironmentResponse +{ +} \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.sln b/Host/Lab.Host.Env/src/Lab.Host.Env.sln new file mode 100644 index 00000000..e4b7fff7 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Host.Env.ConsoleApp", "Lab.Host.Env.ConsoleApp\Lab.Host.Env.ConsoleApp.csproj", "{B7D0D873-72F9-455E-8012-2317E88A20A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Host.Env.WebApi", "Lab.Host.Env.WebApi\Lab.Host.Env.WebApi.csproj", "{C290A555-D176-45BF-B037-1BA36AE7F776}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B7D0D873-72F9-455E-8012-2317E88A20A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7D0D873-72F9-455E-8012-2317E88A20A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7D0D873-72F9-455E-8012-2317E88A20A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7D0D873-72F9-455E-8012-2317E88A20A7}.Release|Any CPU.Build.0 = Release|Any CPU + {C290A555-D176-45BF-B037-1BA36AE7F776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C290A555-D176-45BF-B037-1BA36AE7F776}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C290A555-D176-45BF-B037-1BA36AE7F776}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C290A555-D176-45BF-B037-1BA36AE7F776}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From af30d60b739800e34ba3a56f14de48f9c73d6248 Mon Sep 17 00:00:00 2001 From: yao Date: Sat, 27 Aug 2022 00:20:11 +0800 Subject: [PATCH 259/267] =?UTF-8?q?=E5=88=87=E6=8F=9B=E8=A8=AD=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs | 12 +++++++++++- .../Lab.Host.Env.WebApi/appsettings.Development.json | 7 ++----- .../src/Lab.Host.Env.WebApi/appsettings.Staging.json | 5 +++++ .../src/Lab.Host.Env.WebApi/appsettings.json | 5 ++++- 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Staging.json diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs index 329fe361..6b10260d 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs @@ -1,13 +1,22 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. - builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); +var environmentName = builder.Environment.EnvironmentName; +var configRoot = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + ; + +builder.Configuration.AddConfiguration(configRoot); var app = builder.Build(); // Configure the HTTP request pipeline. @@ -17,6 +26,7 @@ app.UseSwaggerUI(); } +var extension = app.Configuration.GetSection("Extension:Version"); app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json index 0c208ae9..5fcf250c 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Development.json @@ -1,8 +1,5 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } + "Extension": { + "Version": "Development" } } diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Staging.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Staging.json new file mode 100644 index 00000000..d7c762b5 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Staging.json @@ -0,0 +1,5 @@ +{ + "Extension": { + "Version": "Staging" + } +} \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json index 10f68b8c..fe523927 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.json @@ -5,5 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Extension": { + "Version": "Default" + } } From 38e7cb07ce6e1ee14890ba9f7c51001e48676513 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 28 Aug 2022 10:59:51 +0800 Subject: [PATCH 260/267] add file --- Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs | 8 +++++--- .../src/Lab.Host.Env.WebApi/appsettings.Production.json | 5 +++++ Host/Lab.Host.Env/src/Lab.Host.Env.sln | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Production.json diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs index 6b10260d..7e2863ed 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/Program.cs @@ -11,10 +11,9 @@ var environmentName = builder.Environment.EnvironmentName; var configRoot = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) .Build(); - ; builder.Configuration.AddConfiguration(configRoot); var app = builder.Build(); @@ -26,7 +25,10 @@ app.UseSwaggerUI(); } -var extension = app.Configuration.GetSection("Extension:Version"); +var version = app.Configuration.GetSection("Extension:Version").Value; +Console.WriteLine($"Environment: {app.Environment.EnvironmentName}"); +Console.WriteLine($"Extension.Version: {version}"); + app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Production.json b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Production.json new file mode 100644 index 00000000..7d4ee65a --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.WebApi/appsettings.Production.json @@ -0,0 +1,5 @@ +{ + "Extension": { + "Version": "Production" + } +} diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.sln b/Host/Lab.Host.Env/src/Lab.Host.Env.sln index e4b7fff7..5e559ea5 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.sln +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.sln @@ -4,6 +4,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Host.Env.ConsoleApp", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.Host.Env.WebApi", "Lab.Host.Env.WebApi\Lab.Host.Env.WebApi.csproj", "{C290A555-D176-45BF-B037-1BA36AE7F776}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{4601CB1B-259A-415E-B349-76EA4759821A}" + ProjectSection(SolutionItems) = preProject + ..\Taskfile.yml = ..\Taskfile.yml + ..\.env = ..\.env + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 7d3e8caf0b601425f02a9058ca4481353f285a9f Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 28 Aug 2022 11:00:30 +0800 Subject: [PATCH 261/267] add file --- Host/Lab.Host.Env/.env | 1 + Host/Lab.Host.Env/Taskfile.yml | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 Host/Lab.Host.Env/.env create mode 100644 Host/Lab.Host.Env/Taskfile.yml diff --git a/Host/Lab.Host.Env/.env b/Host/Lab.Host.Env/.env new file mode 100644 index 00000000..403721d0 --- /dev/null +++ b/Host/Lab.Host.Env/.env @@ -0,0 +1 @@ +ASPNETCORE_ENVIRONMENT=Staging \ No newline at end of file diff --git a/Host/Lab.Host.Env/Taskfile.yml b/Host/Lab.Host.Env/Taskfile.yml new file mode 100644 index 00000000..5eb0571a --- /dev/null +++ b/Host/Lab.Host.Env/Taskfile.yml @@ -0,0 +1,132 @@ +# Taskfile.yml + +version: "3" + +dotenv: [ "secrets/secrets.env" ] + +tasks: + + api-dev: + desc: WebApi Development + dir: "src/NineYi.Msa.MemberService.WebAPI" + cmds: + - dotnet watch run --local {{.CLI_ARGS}} | jq -rR '. as $line | try (fromjson | "\(._ts) | \(._lvl) | \(._hid)-\(._ver) | \(._srctx)", ._msg, ._props) catch $line' + + dm-dev: + desc: DataMigration Development. Usage => task dm-dev -- diff -s 2001-01-01 -e 2022-01-01 + dir: "src/NineYi.Msa.MemberService.DataMigration.ConsoleApp" + cmds: + - dotnet watch run {{.CLI_ARGS}} --local true | jq -rR '. as $line | try (fromjson | "\(._ts) | \(._lvl) | \(._hid)-\(._ver) | \(._srctx)", ._msg, ._props) catch $line' + + dm-run: + desc: DataMigration run. Usage => task dm-run -- diff -s 2001-01-01 -e 2022-01-01 + dir: "src/NineYi.Msa.MemberService.DataMigration.ConsoleApp" + cmds: + - dotnet run {{.CLI_ARGS}} --local true + + db-start: + desc: start PostgreSQL at local + cmds: + - docker-compose up -d --remove-orphans db + + db-update: + desc: apply db migration on local PostgreSQL + cmds: + - dotnet ef database update {{.CLI_ARGS}} --project src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext + + redis-start: + desc: start redis 5.X version + cmds: + - docker-compose up -d redis + + redis-admin-start: + desc: admin ui to manage redis + cmds: + - docker-compose up -d redis-admin + + db-otm-db-start: + dir: test/NineYi.Msa.MemberService.NMQ3.Worker.Infrastructure.Tests + cmds: + - docker run --privileged -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=pass@w0rd1~' -p 1433:1433 -d datagrip/mssql-server-linux + + db-otm-db-update: + dir: test/NineYi.Msa.MemberService.NMQ3.Worker.Infrastructure.Tests + cmds: + - dotnet build + - dotnet ef database update --context CrmDbContext --no-build + - dotnet ef database update --context WebStoreDbContext --no-build + + db-mssql-start: + desc: start MSSQL at local + cmds: + - docker-compose up -d db-sql + + db-mssql-update: + desc: db update on local MSSQL + dir: src/NineYi.Msa.MemberService.DataMigration.Infrastructure + cmds: + - dotnet build + - dotnet ef database update --context CrmDbContext --no-build + - dotnet ef database update --context WebStoreDbContext --no-build + + db-list-migrations: + desc: member-service list migrations + cmds: + - dotnet ef migrations list {{.CLI_ARGS}} -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext + + db-add-migration: + desc: member-service add migration + cmds: + - dotnet ef migrations add {{.CLI_ARGS}} -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext + + db-remove-migration: + desc: member-service remove migration + cmds: + - dotnet ef migrations remove -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext + + db-script: + desc: generate member-service db script + cmds: + - dotnet ef migrations script 0 -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext + + ddb-start: + desc: start dynamodb at local + cmds: + - docker-compose up -d ddb + + ddb-clear: + desc: clear ddb table + cmds: + - docker-compose restart ddb + - aws dynamodb create-table --endpoint-url http://localhost:8000 --cli-input-json file://./ddb.json + + ddb-init: + desc: init dynamodb table at local + cmds: + - aws dynamodb create-table --endpoint-url http://localhost:8000 --cli-input-json file://./ddb.json + + ddb-list: + desc: list dynamodb tables at local + cmds: + - aws dynamodb list-tables --endpoint-url http://localhost:8000 + + ddb-logs: + cmds: + - docker-compose logs -f ddb + + ddb-admin-start: + desc: start dynamodb admin + cmds: + - docker-compose up -d ddb-admin + + s3-minio-start: + desc: start s3-minio at local + cmds: + - docker-compose up -d s3-minio + + gen-k8s: + desc: generate k8s + cmds: + - dotnet run --project src/NineYi.Msa.K8S.Template.GeneratorApp -- -s $(pwd)/env -d $(pwd)/env/k8s -v 1.0.0.1 + + From dc9d588f81e757c8ca13d2bf11e0aa68d3b86331 Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 28 Aug 2022 23:24:29 +0800 Subject: [PATCH 262/267] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20webapi=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Host/Lab.Host.Env/Taskfile.yml | 126 +-------------------------------- 1 file changed, 3 insertions(+), 123 deletions(-) diff --git a/Host/Lab.Host.Env/Taskfile.yml b/Host/Lab.Host.Env/Taskfile.yml index 5eb0571a..b410b262 100644 --- a/Host/Lab.Host.Env/Taskfile.yml +++ b/Host/Lab.Host.Env/Taskfile.yml @@ -5,128 +5,8 @@ version: "3" dotenv: [ "secrets/secrets.env" ] tasks: - - api-dev: + webapi: desc: WebApi Development - dir: "src/NineYi.Msa.MemberService.WebAPI" - cmds: - - dotnet watch run --local {{.CLI_ARGS}} | jq -rR '. as $line | try (fromjson | "\(._ts) | \(._lvl) | \(._hid)-\(._ver) | \(._srctx)", ._msg, ._props) catch $line' - - dm-dev: - desc: DataMigration Development. Usage => task dm-dev -- diff -s 2001-01-01 -e 2022-01-01 - dir: "src/NineYi.Msa.MemberService.DataMigration.ConsoleApp" - cmds: - - dotnet watch run {{.CLI_ARGS}} --local true | jq -rR '. as $line | try (fromjson | "\(._ts) | \(._lvl) | \(._hid)-\(._ver) | \(._srctx)", ._msg, ._props) catch $line' - - dm-run: - desc: DataMigration run. Usage => task dm-run -- diff -s 2001-01-01 -e 2022-01-01 - dir: "src/NineYi.Msa.MemberService.DataMigration.ConsoleApp" - cmds: - - dotnet run {{.CLI_ARGS}} --local true - - db-start: - desc: start PostgreSQL at local - cmds: - - docker-compose up -d --remove-orphans db - - db-update: - desc: apply db migration on local PostgreSQL - cmds: - - dotnet ef database update {{.CLI_ARGS}} --project src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext - - redis-start: - desc: start redis 5.X version - cmds: - - docker-compose up -d redis - - redis-admin-start: - desc: admin ui to manage redis - cmds: - - docker-compose up -d redis-admin - - db-otm-db-start: - dir: test/NineYi.Msa.MemberService.NMQ3.Worker.Infrastructure.Tests - cmds: - - docker run --privileged -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=pass@w0rd1~' -p 1433:1433 -d datagrip/mssql-server-linux - - db-otm-db-update: - dir: test/NineYi.Msa.MemberService.NMQ3.Worker.Infrastructure.Tests - cmds: - - dotnet build - - dotnet ef database update --context CrmDbContext --no-build - - dotnet ef database update --context WebStoreDbContext --no-build - - db-mssql-start: - desc: start MSSQL at local - cmds: - - docker-compose up -d db-sql - - db-mssql-update: - desc: db update on local MSSQL - dir: src/NineYi.Msa.MemberService.DataMigration.Infrastructure - cmds: - - dotnet build - - dotnet ef database update --context CrmDbContext --no-build - - dotnet ef database update --context WebStoreDbContext --no-build - - db-list-migrations: - desc: member-service list migrations + dir: "src/Lab.Host.Env.WebApi" cmds: - - dotnet ef migrations list {{.CLI_ARGS}} -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext - - db-add-migration: - desc: member-service add migration - cmds: - - dotnet ef migrations add {{.CLI_ARGS}} -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext - - db-remove-migration: - desc: member-service remove migration - cmds: - - dotnet ef migrations remove -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext - - db-script: - desc: generate member-service db script - cmds: - - dotnet ef migrations script 0 -p src/NineYi.Msa.MemberService.Infrastructure.DB --context MemberDbContext - - ddb-start: - desc: start dynamodb at local - cmds: - - docker-compose up -d ddb - - ddb-clear: - desc: clear ddb table - cmds: - - docker-compose restart ddb - - aws dynamodb create-table --endpoint-url http://localhost:8000 --cli-input-json file://./ddb.json - - ddb-init: - desc: init dynamodb table at local - cmds: - - aws dynamodb create-table --endpoint-url http://localhost:8000 --cli-input-json file://./ddb.json - - ddb-list: - desc: list dynamodb tables at local - cmds: - - aws dynamodb list-tables --endpoint-url http://localhost:8000 - - ddb-logs: - cmds: - - docker-compose logs -f ddb - - ddb-admin-start: - desc: start dynamodb admin - cmds: - - docker-compose up -d ddb-admin - - s3-minio-start: - desc: start s3-minio at local - cmds: - - docker-compose up -d s3-minio - - gen-k8s: - desc: generate k8s - cmds: - - dotnet run --project src/NineYi.Msa.K8S.Template.GeneratorApp -- -s $(pwd)/env -d $(pwd)/env/k8s -v 1.0.0.1 - - + - dotnet run --environment Staging \ No newline at end of file From 6b9b3b1e35a75d9cf0abd4a6ba1790a36ba724f7 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 29 Aug 2022 00:15:13 +0800 Subject: [PATCH 263/267] add console app --- Host/Lab.Host.Env/Taskfile.yml | 7 ++++- .../Lab.Host.Env.ConsoleApp.csproj | 27 +++++++++++++++++++ .../src/Lab.Host.Env.ConsoleApp/Program.cs | 22 +++++++++++++++ .../appsettings.Development.json | 5 ++++ .../appsettings.Production.json | 5 ++++ .../appsettings.Staging.json | 5 ++++ .../Lab.Host.Env.ConsoleApp/appsettings.json | 12 +++++++++ 7 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Development.json create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Production.json create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Staging.json create mode 100644 Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.json diff --git a/Host/Lab.Host.Env/Taskfile.yml b/Host/Lab.Host.Env/Taskfile.yml index b410b262..e0166fd1 100644 --- a/Host/Lab.Host.Env/Taskfile.yml +++ b/Host/Lab.Host.Env/Taskfile.yml @@ -9,4 +9,9 @@ tasks: desc: WebApi Development dir: "src/Lab.Host.Env.WebApi" cmds: - - dotnet run --environment Staging \ No newline at end of file + - dotnet run --environment Staging + app: + desc: WebApi Development + dir: "src/Lab.Host.Env.ConsoleApp" + cmds: + - dotnet run --environment Production \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj index b9de0634..100dd407 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Lab.Host.Env.ConsoleApp.csproj @@ -7,4 +7,31 @@ enable + + + + + + + true + PreserveNewest + PreserveNewest + + + true + PreserveNewest + PreserveNewest + + + true + PreserveNewest + PreserveNewest + + + true + PreserveNewest + PreserveNewest + + + diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs index e5dff12b..5f110bd6 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/Program.cs @@ -1,3 +1,25 @@ // See https://aka.ms/new-console-template for more information +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +var hostBuilder = Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostContext, config) => + { + var environmentName = hostContext.HostingEnvironment.EnvironmentName; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + config.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true); + }) + ; +var host = hostBuilder.Build(); +var environment = host.Services.GetService(); +Console.WriteLine($"Environment: {environment.EnvironmentName}"); + +var configuration = host.Services.GetService(); +var version = configuration.GetSection("Extension:Version").Value; +Console.WriteLine($"Extension.Version: {version}"); + +await host.StartAsync(); +await host.StopAsync(); Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Development.json b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Development.json new file mode 100644 index 00000000..5fcf250c --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Development.json @@ -0,0 +1,5 @@ +{ + "Extension": { + "Version": "Development" + } +} diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Production.json b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Production.json new file mode 100644 index 00000000..7d4ee65a --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Production.json @@ -0,0 +1,5 @@ +{ + "Extension": { + "Version": "Production" + } +} diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Staging.json b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Staging.json new file mode 100644 index 00000000..d7c762b5 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.Staging.json @@ -0,0 +1,5 @@ +{ + "Extension": { + "Version": "Staging" + } +} \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.json b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.json new file mode 100644 index 00000000..fe523927 --- /dev/null +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.ConsoleApp/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Extension": { + "Version": "Default" + } +} From 07a1e4bcbbbfb99f773b914c69c039943ae3dc99 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 29 Aug 2022 13:27:40 +0800 Subject: [PATCH 264/267] add file --- Host/Lab.Host.Env/.gitignore | 350 +++++++++++++++++++++++++ Host/Lab.Host.Env/src/Lab.Host.Env.sln | 1 + 2 files changed, 351 insertions(+) create mode 100644 Host/Lab.Host.Env/.gitignore diff --git a/Host/Lab.Host.Env/.gitignore b/Host/Lab.Host.Env/.gitignore new file mode 100644 index 00000000..81c554f7 --- /dev/null +++ b/Host/Lab.Host.Env/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +#vistual studio extension +.localhistory + +# stress test output +stress/output + +# specflow feature.cs +**/*.feature.cs + +# secrets +secrets + +.DS_Store +*.zip + +deployments + +# minio local s3 +minio \ No newline at end of file diff --git a/Host/Lab.Host.Env/src/Lab.Host.Env.sln b/Host/Lab.Host.Env/src/Lab.Host.Env.sln index 5e559ea5..3a78e837 100644 --- a/Host/Lab.Host.Env/src/Lab.Host.Env.sln +++ b/Host/Lab.Host.Env/src/Lab.Host.Env.sln @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{4601CB ProjectSection(SolutionItems) = preProject ..\Taskfile.yml = ..\Taskfile.yml ..\.env = ..\.env + ..\.gitignore = ..\.gitignore EndProjectSection EndProject Global From 776be876b630456849117177f862b8b22cca258b Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 29 Aug 2022 13:30:02 +0800 Subject: [PATCH 265/267] app project --- StructLog/Lab.SerilogProject/.gitignore | 350 ++++++++++++++++++ StructLog/Lab.SerilogProject/Taskfile.yml | 17 + .../Controllers/WeatherForecastController.cs | 32 ++ .../Lab.SerilogProject.WebApi.csproj | 13 + .../Lab.SerilogProject.WebApi/Program.cs | 26 ++ .../WeatherForecast.cs | 12 + .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../Lab.SerilogProject/Lab.SerilogProject.sln | 22 ++ 9 files changed, 489 insertions(+) create mode 100644 StructLog/Lab.SerilogProject/.gitignore create mode 100644 StructLog/Lab.SerilogProject/Taskfile.yml create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Controllers/WeatherForecastController.cs create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/WeatherForecast.cs create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.Development.json create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.json create mode 100644 StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.sln diff --git a/StructLog/Lab.SerilogProject/.gitignore b/StructLog/Lab.SerilogProject/.gitignore new file mode 100644 index 00000000..81c554f7 --- /dev/null +++ b/StructLog/Lab.SerilogProject/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +#vistual studio extension +.localhistory + +# stress test output +stress/output + +# specflow feature.cs +**/*.feature.cs + +# secrets +secrets + +.DS_Store +*.zip + +deployments + +# minio local s3 +minio \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/Taskfile.yml b/StructLog/Lab.SerilogProject/Taskfile.yml new file mode 100644 index 00000000..3ce2218e --- /dev/null +++ b/StructLog/Lab.SerilogProject/Taskfile.yml @@ -0,0 +1,17 @@ +# Taskfile.yml + +version: "3" + +dotenv: [ "secrets/secrets.env" ] + +tasks: + webapi: + desc: WebApi Development + dir: "src/Lab.SerilogProject.WebApi" + cmds: + - dotnet run --environment Staging + app: + desc: WebApi Development + dir: "src/Lab.SerilogProject.ConsoleApp" + cmds: + - dotnet run --environment Production \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Controllers/WeatherForecastController.cs b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..7bb79242 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lab.SerilogProject.WebApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj new file mode 100644 index 00000000..6108b7b2 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs new file mode 100644 index 00000000..329fe361 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs @@ -0,0 +1,26 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/WeatherForecast.cs b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/WeatherForecast.cs new file mode 100644 index 00000000..6bf80410 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Lab.SerilogProject.WebApi; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.Development.json b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.json b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.sln b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.sln new file mode 100644 index 00000000..263368a0 --- /dev/null +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SerilogProject.WebApi", "Lab.SerilogProject.WebApi\Lab.SerilogProject.WebApi.csproj", "{D58F5F77-C34C-4971-B044-565BED8C9A5D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{57242756-8814-45F1-A9E3-474721C382BC}" + ProjectSection(SolutionItems) = preProject + ..\..\.gitignore = ..\..\.gitignore + ..\..\Taskfile.yml = ..\..\Taskfile.yml + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D58F5F77-C34C-4971-B044-565BED8C9A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D58F5F77-C34C-4971-B044-565BED8C9A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D58F5F77-C34C-4971-B044-565BED8C9A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D58F5F77-C34C-4971-B044-565BED8C9A5D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 78c5f311b380531f8280704f51db94b35572514d Mon Sep 17 00:00:00 2001 From: yao Date: Sun, 4 Sep 2022 09:36:49 +0800 Subject: [PATCH 266/267] set serilog config --- StructLog/Lab.SerilogProject/Taskfile.yml | 7 +- .../Lab.SerilogProject.WebApi.csproj | 8 ++- .../Lab.SerilogProject.WebApi/Program.cs | 69 ++++++++++++++----- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/StructLog/Lab.SerilogProject/Taskfile.yml b/StructLog/Lab.SerilogProject/Taskfile.yml index 3ce2218e..e5b8cef3 100644 --- a/StructLog/Lab.SerilogProject/Taskfile.yml +++ b/StructLog/Lab.SerilogProject/Taskfile.yml @@ -14,4 +14,9 @@ tasks: desc: WebApi Development dir: "src/Lab.SerilogProject.ConsoleApp" cmds: - - dotnet run --environment Production \ No newline at end of file + - dotnet run --environment Production + + seq-start: + desc: start seq service + cmds: + - docker run --name seq -d --restart unless-stopped -e ACCEPT_EULA=Y -p 5341:80 datalust/seq:latest \ No newline at end of file diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj index 6108b7b2..458e3800 100644 --- a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Lab.SerilogProject.WebApi.csproj @@ -7,7 +7,13 @@ - + + + + + + + diff --git a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs index 329fe361..bebb259c 100644 --- a/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs +++ b/StructLog/Lab.SerilogProject/src/Lab.SerilogProject/Lab.SerilogProject.WebApi/Program.cs @@ -1,26 +1,61 @@ -var builder = WebApplication.CreateBuilder(args); +using Serilog; +using Serilog.Events; + +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.File("logs/app-.txt", rollingInterval: RollingInterval.Day) + .CreateLogger(); +try +{ + Log.Information("Starting web host"); -// Add services to the container. + var builder = WebApplication.CreateBuilder(args); -builder.Services.AddControllers(); + // builder.Host.UseSerilog(); //<=== 讓 Host 使用 Serilog + builder.Host.UseSerilog((context, services, config) => + config.ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq("http://localhost:5341") + // .WriteTo.File("logs/aspnet-.txt", rollingInterval: RollingInterval.Minute) + ); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); + // Add services to the container. -var app = builder.Build(); + builder.Services.AddControllers(); -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.UseSwagger(); - app.UseSwaggerUI(); -} + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + var app = builder.Build(); -app.UseHttpsRedirection(); + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } -app.UseAuthorization(); + app.UseHttpsRedirection(); + app.UseSerilogRequestLogging(); //<=== 每一個 Request 使用 Serilog 記錄下來 + app.UseAuthorization(); -app.MapControllers(); + app.MapControllers(); -app.Run(); \ No newline at end of file + app.Run(); + return 0; +} +catch (Exception ex) +{ + Log.Fatal(ex, "Host terminated unexpectedly"); + return 1; +} +finally +{ + Log.CloseAndFlush(); +} \ No newline at end of file From 9cc04a7b92022c199f8db2544c19306f7b0fb8aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Sep 2022 01:37:29 +0000 Subject: [PATCH 267/267] build(deps): bump Microsoft.Owin Bumps [Microsoft.Owin](https://github.com/aspnet/AspNetKatana) from 4.1.0 to 4.2.2. - [Release notes](https://github.com/aspnet/AspNetKatana/releases) - [Commits](https://github.com/aspnet/AspNetKatana/compare/v4.1.0...v4.2.2) --- updated-dependencies: - dependency-name: Microsoft.Owin dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../Lab.HangfireManager.AspNet48/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hangfire/Lab.HangfireManager/Lab.HangfireManager.AspNet48/packages.config b/Hangfire/Lab.HangfireManager/Lab.HangfireManager.AspNet48/packages.config index 941d3ea0..b3dd1fd9 100644 --- a/Hangfire/Lab.HangfireManager/Lab.HangfireManager.AspNet48/packages.config +++ b/Hangfire/Lab.HangfireManager/Lab.HangfireManager.AspNet48/packages.config @@ -13,7 +13,7 @@ - +