diff --git a/AWS/Lab.AwsDDB/Lab.AwsDDB.sln b/AWS/Lab.AwsDDB/Lab.AwsDDB.sln
new file mode 100644
index 00000000..8c7ba129
--- /dev/null
+++ b/AWS/Lab.AwsDDB/Lab.AwsDDB.sln
@@ -0,0 +1,21 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.AwsDDB.Test", "test\Lab.AwsDDB.Test\Lab.AwsDDB.Test.csproj", "{5B2F9C1D-597C-4890-9806-42B42D42F15C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{23EBD672-F330-4AEB-9F14-24C0E562F670}"
+ 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
+ {5B2F9C1D-597C-4890-9806-42B42D42F15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5B2F9C1D-597C-4890-9806-42B42D42F15C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5B2F9C1D-597C-4890-9806-42B42D42F15C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5B2F9C1D-597C-4890-9806-42B42D42F15C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/AWS/Lab.AwsDDB/docker-compose.yml b/AWS/Lab.AwsDDB/docker-compose.yml
new file mode 100644
index 00000000..4dd4623e
--- /dev/null
+++ b/AWS/Lab.AwsDDB/docker-compose.yml
@@ -0,0 +1,17 @@
+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
\ No newline at end of file
diff --git a/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Lab.AwsDDB.Test.csproj b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Lab.AwsDDB.Test.csproj
new file mode 100644
index 00000000..24fa062f
--- /dev/null
+++ b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Lab.AwsDDB.Test.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/UnitTest1.cs b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/UnitTest1.cs
new file mode 100644
index 00000000..cddd0eb9
--- /dev/null
+++ b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/UnitTest1.cs
@@ -0,0 +1,524 @@
+using Amazon.DynamoDBv2;
+using Amazon.DynamoDBv2.DocumentModel;
+using Amazon.DynamoDBv2.Model;
+
+namespace Lab.AwsDDB.Test;
+
+[TestClass]
+public class UnitTest1
+{
+ [TestMethod]
+ public void TestMethod1()
+ {
+ var clientConfig = new AmazonDynamoDBConfig();
+ clientConfig.ServiceURL = "http://localhost:8000";
+ var client = new AmazonDynamoDBClient(clientConfig);
+ // CreateTableProductCatalog(client);
+ LoadSampleProducts(client);
+ }
+
+ // static void Main(string[] args)
+ // {
+ // try
+ // {
+ // //DeleteAllTables(client);
+ // DeleteTable("ProductCatalog");
+ // DeleteTable("Forum");
+ // DeleteTable("Thread");
+ // DeleteTable("Reply");
+ //
+ // // Create tables (using the AWS SDK for .NET low-level API).
+ // CreateTableProductCatalog();
+ // CreateTableForum();
+ // CreateTableThread(); // ForumTitle, Subject */
+ // CreateTableReply();
+ //
+ // // Load data (using the .NET SDK document API)
+ // LoadSampleProducts();
+ // LoadSampleForums();
+ // LoadSampleThreads();
+ // LoadSampleReplies();
+ // Console.WriteLine("Sample complete!");
+ // Console.WriteLine("Press ENTER to continue");
+ // Console.ReadLine();
+ // }
+ // catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
+ // catch (Exception e) { Console.WriteLine(e.Message); }
+ // }
+
+ private static async Task DeleteTable(AmazonDynamoDBClient client, string tableName)
+ {
+ try
+ {
+ var deleteTableResponse = await client.DeleteTableAsync(new DeleteTableRequest()
+ {
+ TableName = tableName
+ });
+ WaitTillTableDeleted(client, tableName, deleteTableResponse);
+ }
+ catch (ResourceNotFoundException)
+ {
+ // There is no such table.
+ }
+ }
+
+ private static async Task CreateTableProductCatalog(AmazonDynamoDBClient client)
+ {
+ string tableName = "ProductCatalog";
+
+ var response = await client.CreateTableAsync(new CreateTableRequest
+ {
+ TableName = tableName,
+ AttributeDefinitions = new List()
+ {
+ new()
+ {
+ AttributeName = "Id",
+ AttributeType = "N"
+ }
+ },
+ KeySchema = new List()
+ {
+ new()
+ {
+ AttributeName = "Id",
+ KeyType = "HASH"
+ }
+ },
+ ProvisionedThroughput = new ProvisionedThroughput
+ {
+ ReadCapacityUnits = 10,
+ WriteCapacityUnits = 5
+ }
+ });
+
+ WaitTillTableCreated(client, tableName, response);
+ }
+
+ private static async Task CreateTableForum(AmazonDynamoDBClient client)
+ {
+ string tableName = "Forum";
+
+ var response = await client.CreateTableAsync(new CreateTableRequest
+ {
+ TableName = tableName,
+ AttributeDefinitions = new List()
+ {
+ new AttributeDefinition
+ {
+ AttributeName = "Name",
+ AttributeType = "S"
+ }
+ },
+ KeySchema = new List()
+ {
+ new KeySchemaElement
+ {
+ AttributeName = "Name", // forum Title
+ KeyType = "HASH"
+ }
+ },
+ ProvisionedThroughput = new ProvisionedThroughput
+ {
+ ReadCapacityUnits = 10,
+ WriteCapacityUnits = 5
+ }
+ });
+
+ WaitTillTableCreated(client, tableName, response);
+ }
+
+ private static async Task CreateTableThread(AmazonDynamoDBClient client)
+ {
+ string tableName = "Thread";
+
+ var response = await client.CreateTableAsync(new CreateTableRequest
+ {
+ TableName = tableName,
+ AttributeDefinitions = new List()
+ {
+ new AttributeDefinition
+ {
+ AttributeName = "ForumName", // Hash attribute
+ AttributeType = "S"
+ },
+ new AttributeDefinition
+ {
+ AttributeName = "Subject",
+ AttributeType = "S"
+ }
+ },
+ KeySchema = new List()
+ {
+ new KeySchemaElement
+ {
+ AttributeName = "ForumName", // Hash attribute
+ KeyType = "HASH"
+ },
+ new KeySchemaElement
+ {
+ AttributeName = "Subject", // Range attribute
+ KeyType = "RANGE"
+ }
+ },
+ ProvisionedThroughput = new ProvisionedThroughput
+ {
+ ReadCapacityUnits = 10,
+ WriteCapacityUnits = 5
+ }
+ });
+
+ WaitTillTableCreated(client, tableName, response);
+ }
+
+ private static async Task CreateTableReply(AmazonDynamoDBClient client)
+ {
+ string tableName = "Reply";
+ var response = await client.CreateTableAsync(new CreateTableRequest
+ {
+ TableName = tableName,
+ AttributeDefinitions = new List()
+ {
+ new AttributeDefinition
+ {
+ AttributeName = "Id",
+ AttributeType = "S"
+ },
+ new AttributeDefinition
+ {
+ AttributeName = "ReplyDateTime",
+ AttributeType = "S"
+ },
+ new AttributeDefinition
+ {
+ AttributeName = "PostedBy",
+ AttributeType = "S"
+ }
+ },
+ KeySchema = new List()
+ {
+ new KeySchemaElement()
+ {
+ AttributeName = "Id",
+ KeyType = "HASH"
+ },
+ new KeySchemaElement()
+ {
+ AttributeName = "ReplyDateTime",
+ KeyType = "RANGE"
+ }
+ },
+ LocalSecondaryIndexes = new List()
+ {
+ new LocalSecondaryIndex()
+ {
+ IndexName = "PostedBy_index",
+
+ KeySchema = new List()
+ {
+ new KeySchemaElement()
+ {
+ AttributeName = "Id", KeyType = "HASH"
+ },
+ new KeySchemaElement()
+ {
+ AttributeName = "PostedBy", KeyType = "RANGE"
+ }
+ },
+ Projection = new Projection()
+ {
+ ProjectionType = ProjectionType.KEYS_ONLY
+ }
+ }
+ },
+ ProvisionedThroughput = new ProvisionedThroughput
+ {
+ ReadCapacityUnits = 10,
+ WriteCapacityUnits = 5
+ }
+ });
+
+ WaitTillTableCreated(client, tableName, response);
+ }
+
+ private static async Task WaitTillTableCreated(AmazonDynamoDBClient client,
+ string tableName,
+ CreateTableResponse response)
+ {
+ var tableDescription = response.TableDescription;
+
+ string status = tableDescription.TableStatus;
+
+ Console.WriteLine(tableName + " - " + status);
+
+ // Let us wait until table is created. Call DescribeTable.
+ while (status != "ACTIVE")
+ {
+ System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
+ try
+ {
+ var res = await client.DescribeTableAsync(new DescribeTableRequest
+ {
+ TableName = tableName
+ });
+ Console.WriteLine("Table name: {0}, status: {1}", res.Table.TableName,
+ res.Table.TableStatus);
+ status = res.Table.TableStatus;
+ }
+
+ // Try-catch to handle potential eventual-consistency issue.
+ catch (ResourceNotFoundException)
+ {
+ }
+ }
+ }
+
+ private static async Task WaitTillTableDeleted(AmazonDynamoDBClient client, string tableName,
+ DeleteTableResponse response)
+ {
+ var tableDescription = response.TableDescription;
+
+ string status = tableDescription.TableStatus;
+
+ Console.WriteLine(tableName + " - " + status);
+
+ // Let us wait until table is created. Call DescribeTable
+ try
+ {
+ while (status == "DELETING")
+ {
+ System.Threading.Thread.Sleep(5000); // wait 5 seconds
+
+ var res = await client.DescribeTableAsync(new DescribeTableRequest
+ {
+ TableName = tableName
+ });
+ Console.WriteLine("Table name: {0}, status: {1}", res.Table.TableName,
+ res.Table.TableStatus);
+ status = res.Table.TableStatus;
+ }
+ }
+ catch (ResourceNotFoundException)
+ {
+ // Table deleted.
+ }
+ }
+
+ private static async Task LoadSampleProducts(AmazonDynamoDBClient client)
+ {
+ Table productCatalogTable = Table.LoadTable(client, "ProductCatalog");
+
+ // ********** Add Books *********************
+ var book1 = new Document();
+ book1["Id"] = 101;
+ book1["Title"] = "Book 101 Title";
+ book1["ISBN"] = "111-1111111111";
+ book1["Authors"] = new List { "Author 1" };
+ book1["Price"] = -2; // *** Intentional value. Later used to illustrate scan.
+ book1["Dimensions"] = "8.5 x 11.0 x 0.5";
+ book1["PageCount"] = 500;
+ book1["InPublication"] = true;
+ book1["ProductCategory"] = "Book";
+ await productCatalogTable.PutItemAsync(book1);
+
+ var book2 = new Document();
+
+ book2["Id"] = 102;
+ book2["Title"] = "Book 102 Title";
+ book2["ISBN"] = "222-2222222222";
+ book2["Authors"] = new List { "Author 1", "Author 2" };
+ ;
+ book2["Price"] = 20;
+ book2["Dimensions"] = "8.5 x 11.0 x 0.8";
+ book2["PageCount"] = 600;
+ book2["InPublication"] = true;
+ book2["ProductCategory"] = "Book";
+ await productCatalogTable.PutItemAsync(book2);
+
+ var book3 = new Document();
+ book3["Id"] = 103;
+ book3["Title"] = "Book 103 Title";
+ book3["ISBN"] = "333-3333333333";
+ book3["Authors"] = new List { "Author 1", "Author2", "Author 3" };
+ ;
+ book3["Price"] = 2000;
+ book3["Dimensions"] = "8.5 x 11.0 x 1.5";
+ book3["PageCount"] = 700;
+ book3["InPublication"] = false;
+ book3["ProductCategory"] = "Book";
+ await productCatalogTable.PutItemAsync(book3);
+
+ // ************ Add bikes. *******************
+ var bicycle1 = new Document();
+ bicycle1["Id"] = 201;
+ bicycle1["Title"] = "18-Bike 201"; // size, followed by some title.
+ bicycle1["Description"] = "201 description";
+ bicycle1["BicycleType"] = "Road";
+ bicycle1["Brand"] = "Brand-Company A"; // Trek, Specialized.
+ bicycle1["Price"] = 100;
+ bicycle1["Color"] = new List { "Red", "Black" };
+ bicycle1["ProductCategory"] = "Bike";
+ await productCatalogTable.PutItemAsync(bicycle1);
+
+ var bicycle2 = new Document();
+ bicycle2["Id"] = 202;
+ bicycle2["Title"] = "21-Bike 202Brand-Company A";
+ bicycle2["Description"] = "202 description";
+ bicycle2["BicycleType"] = "Road";
+ bicycle2["Brand"] = "";
+ bicycle2["Price"] = 200;
+ bicycle2["Color"] = new List { "Green", "Black" };
+ bicycle2["ProductCategory"] = "Bicycle";
+ await productCatalogTable.PutItemAsync(bicycle2);
+
+ var bicycle3 = new Document();
+ bicycle3["Id"] = 203;
+ bicycle3["Title"] = "19-Bike 203";
+ bicycle3["Description"] = "203 description";
+ bicycle3["BicycleType"] = "Road";
+ bicycle3["Brand"] = "Brand-Company B";
+ bicycle3["Price"] = 300;
+ bicycle3["Color"] = new List { "Red", "Green", "Black" };
+ bicycle3["ProductCategory"] = "Bike";
+ await productCatalogTable.PutItemAsync(bicycle3);
+
+ var bicycle4 = new Document();
+ bicycle4["Id"] = 204;
+ bicycle4["Title"] = "18-Bike 204";
+ bicycle4["Description"] = "204 description";
+ bicycle4["BicycleType"] = "Mountain";
+ bicycle4["Brand"] = "Brand-Company B";
+ bicycle4["Price"] = 400;
+ bicycle4["Color"] = new List { "Red" };
+ bicycle4["ProductCategory"] = "Bike";
+ await productCatalogTable.PutItemAsync(bicycle4);
+
+ var bicycle5 = new Document();
+ bicycle5["Id"] = 205;
+ bicycle5["Title"] = "20-Title 205";
+ bicycle4["Description"] = "205 description";
+ bicycle5["BicycleType"] = "Hybrid";
+ bicycle5["Brand"] = "Brand-Company C";
+ bicycle5["Price"] = 500;
+ bicycle5["Color"] = new List { "Red", "Black" };
+ bicycle5["ProductCategory"] = "Bike";
+ await productCatalogTable.PutItemAsync(bicycle5);
+ }
+
+ private static async Task LoadSampleForums(AmazonDynamoDBClient client)
+ {
+ Table forumTable = Table.LoadTable(client, "Forum");
+
+ var forum1 = new Document();
+ forum1["Name"] = "Amazon DynamoDB"; // PK
+ forum1["Category"] = "Amazon Web Services";
+ forum1["Threads"] = 2;
+ forum1["Messages"] = 4;
+ forum1["Views"] = 1000;
+
+ await forumTable.PutItemAsync(forum1);
+
+ var forum2 = new Document();
+ forum2["Name"] = "Amazon S3"; // PK
+ forum2["Category"] = "Amazon Web Services";
+ forum2["Threads"] = 1;
+
+ await forumTable.PutItemAsync(forum2);
+ }
+
+ private static async Task LoadSampleThreads(AmazonDynamoDBClient client)
+ {
+ Table threadTable = Table.LoadTable(client, "Thread");
+
+ // Thread 1.
+ var thread1 = new Document();
+ thread1["ForumName"] = "Amazon DynamoDB"; // Hash attribute.
+ thread1["Subject"] = "DynamoDB Thread 1"; // Range attribute.
+ thread1["Message"] = "DynamoDB thread 1 message text";
+ thread1["LastPostedBy"] = "User A";
+ thread1["LastPostedDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(14, 0, 0, 0));
+ thread1["Views"] = 0;
+ thread1["Replies"] = 0;
+ thread1["Answered"] = false;
+ thread1["Tags"] = new List { "index", "primarykey", "table" };
+
+ await threadTable.PutItemAsync(thread1);
+
+ // Thread 2.
+ var thread2 = new Document();
+ thread2["ForumName"] = "Amazon DynamoDB"; // Hash attribute.
+ thread2["Subject"] = "DynamoDB Thread 2"; // Range attribute.
+ thread2["Message"] = "DynamoDB thread 2 message text";
+ thread2["LastPostedBy"] = "User A";
+ thread2["LastPostedDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(21, 0, 0, 0));
+ thread2["Views"] = 0;
+ thread2["Replies"] = 0;
+ thread2["Answered"] = false;
+ thread2["Tags"] = new List { "index", "primarykey", "rangekey" };
+
+ await threadTable.PutItemAsync(thread2);
+
+ // Thread 3.
+ var thread3 = new Document();
+ thread3["ForumName"] = "Amazon S3"; // Hash attribute.
+ thread3["Subject"] = "S3 Thread 1"; // Range attribute.
+ thread3["Message"] = "S3 thread 3 message text";
+ thread3["LastPostedBy"] = "User A";
+ thread3["LastPostedDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(7, 0, 0, 0));
+ thread3["Views"] = 0;
+ thread3["Replies"] = 0;
+ thread3["Answered"] = false;
+ thread3["Tags"] = new List { "largeobjects", "multipart upload" };
+ await threadTable.PutItemAsync(thread3);
+ }
+
+ private static async Task LoadSampleReplies(AmazonDynamoDBClient client)
+ {
+ Table replyTable = Table.LoadTable(client, "Reply");
+
+ // Reply 1 - thread 1.
+ var thread1Reply1 = new Document();
+ thread1Reply1["Id"] = "Amazon DynamoDB#DynamoDB Thread 1"; // Hash attribute.
+ thread1Reply1["ReplyDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(21, 0, 0, 0)); // Range attribute.
+ thread1Reply1["Message"] = "DynamoDB Thread 1 Reply 1 text";
+ thread1Reply1["PostedBy"] = "User A";
+
+ await replyTable.PutItemAsync(thread1Reply1);
+
+ // Reply 2 - thread 1.
+ var thread1reply2 = new Document();
+ thread1reply2["Id"] = "Amazon DynamoDB#DynamoDB Thread 1"; // Hash attribute.
+ thread1reply2["ReplyDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(14, 0, 0, 0)); // Range attribute.
+ thread1reply2["Message"] = "DynamoDB Thread 1 Reply 2 text";
+ thread1reply2["PostedBy"] = "User B";
+
+ await replyTable.PutItemAsync(thread1reply2);
+
+ // Reply 3 - thread 1.
+ var thread1Reply3 = new Document();
+ thread1Reply3["Id"] = "Amazon DynamoDB#DynamoDB Thread 1"; // Hash attribute.
+ thread1Reply3["ReplyDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(7, 0, 0, 0)); // Range attribute.
+ thread1Reply3["Message"] = "DynamoDB Thread 1 Reply 3 text";
+ thread1Reply3["PostedBy"] = "User B";
+
+ await replyTable.PutItemAsync(thread1Reply3);
+
+ // Reply 1 - thread 2.
+ var thread2Reply1 = new Document();
+ thread2Reply1["Id"] = "Amazon DynamoDB#DynamoDB Thread 2"; // Hash attribute.
+ thread2Reply1["ReplyDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(7, 0, 0, 0)); // Range attribute.
+ thread2Reply1["Message"] = "DynamoDB Thread 2 Reply 1 text";
+ thread2Reply1["PostedBy"] = "User A";
+
+ await replyTable.PutItemAsync(thread2Reply1);
+
+ // Reply 2 - thread 2.
+ var thread2Reply2 = new Document();
+ thread2Reply2["Id"] = "Amazon DynamoDB#DynamoDB Thread 2"; // Hash attribute.
+ thread2Reply2["ReplyDateTime"] = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0)); // Range attribute.
+ thread2Reply2["Message"] = "DynamoDB Thread 2 Reply 2 text";
+ thread2Reply2["PostedBy"] = "User A";
+
+ await replyTable.PutItemAsync(thread2Reply2);
+ }
+}
\ No newline at end of file
diff --git a/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Usings.cs b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Usings.cs
new file mode 100644
index 00000000..ab67c7ea
--- /dev/null
+++ b/AWS/Lab.AwsDDB/test/Lab.AwsDDB.Test/Usings.cs
@@ -0,0 +1 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
\ No newline at end of file
diff --git a/AWS/Lab.AwsS3/.gitignore b/AWS/Lab.AwsS3/.gitignore
new file mode 100644
index 00000000..eab69e5c
--- /dev/null
+++ b/AWS/Lab.AwsS3/.gitignore
@@ -0,0 +1,1340 @@
+### ASPNETCore template
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# 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 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# 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
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.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
+
+# 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
+
+# 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
+# TODO: 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
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable 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
+
+# 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
+node_modules/
+orleans.codegen.cs
+
+# 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
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# 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/
+
+### Csharp 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/main/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
+*.tlog
+*.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 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# 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/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# 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
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+
+### ASPNETCore template
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# 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 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# 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
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.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
+
+# 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
+
+# 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
+# TODO: 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
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable 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
+
+# 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
+node_modules/
+orleans.codegen.cs
+
+# 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
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# 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/
+
+### Csharp 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/main/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
+*.tlog
+*.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 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# 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/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# 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
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+
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..55b27f74
--- /dev/null
+++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/Lab.Aws.S3.MinIOS3.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
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..331255cf
--- /dev/null
+++ b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/UnitTest1.cs
@@ -0,0 +1,341 @@
+using System.Collections.Concurrent;
+using System.Reflection;
+using Amazon.Runtime;
+using Amazon.S3;
+using Amazon.S3.Model;
+using Amazon.S3.Transfer;
+using Testcontainers.Minio;
+
+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 新增一個儲存桶()
+ {
+ var s3Client = CreateS3Client();
+ var response = await s3Client.PutBucketAsync(new PutBucketRequest
+ {
+ BucketName = "test-bucket",
+ });
+ }
+
+ [TestMethod]
+ public async Task 新增一個儲存桶_For_TestContainer()
+ {
+ var s3Container = await CreateS3Container();
+ var connectionString = s3Container.GetConnectionString();
+ var s3Client = CreateS3Client(connectionString);
+ var response = await s3Client.PutBucketAsync(new PutBucketRequest
+ {
+ BucketName = "test-bucket",
+ });
+ }
+ [TestMethod]
+ public async Task 上傳檔案到儲存桶_For_TestContainer()
+ {
+ var s3Container = await CreateS3Container();
+ var connectionString = s3Container.GetConnectionString();
+ var s3Client = CreateS3Client(connectionString);
+ var bucketName = "test-bucket";
+ var createBucket = await s3Client.PutBucketAsync(new PutBucketRequest
+ {
+ BucketName = bucketName,
+ });
+
+ var inputMemory = new MemoryStream();
+ await File.Open("上傳.csv", FileMode.Open).CopyToAsync(inputMemory);
+ var putObjectResponse = await s3Client.PutObjectAsync(new PutObjectRequest
+ {
+ BucketName = bucketName,
+ Key = "上傳.csv",
+ InputStream = inputMemory,
+ AutoCloseStream = false,
+ AutoResetStreamPosition = true,
+ });
+ var getObjectResponse = await s3Client.GetObjectAsync(new GetObjectRequest
+ {
+ BucketName = bucketName,
+ Key = "上傳.csv",
+ });
+ await using var outputStream = File.Create("下載.csv");
+ await getObjectResponse.ResponseStream.CopyToAsync(outputStream);
+ }
+
+ private static async Task CreateS3Container()
+ {
+ var username = "AKIAIOSFODNN7EXAMPLE";
+ var password = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
+ var minioBuilder = new MinioBuilder()
+ .WithName("minio")
+ .WithHostname("localhost")
+ // .WithImage("quay.io/minio/minio:latest")
+ // .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(9000))
+ .WithUsername(username)
+ .WithPassword(password)
+ .WithPortBinding(9000, assignRandomHostPort: true)
+ // .WithPortBinding(9001, assignRandomHostPort: false)
+ ;
+ var minioContainer = minioBuilder.Build();
+ await minioContainer.StartAsync().ConfigureAwait(false);
+ return minioContainer;
+ }
+
+ [TestMethod]
+ public async Task 分批上傳檔案()
+ {
+ await WriteObjectDataAsync("上傳.csv");
+ }
+
+ [TestMethod]
+ public async Task 分批下載檔案()
+ {
+ await ReadObjectDataAsync2("上傳.csv", "下載.csv");
+ }
+
+ static async Task ReadObjectDataAsync2(string sourceFile, string outFile)
+ {
+ var bucketName = "test-bucket";
+ var key = sourceFile;
+
+ try
+ {
+ var s3Client = CreateS3Client();
+
+ long startPosition = 0;
+ long chunkSize = 4096; // 每次讀取的大小為 4096 bytes
+
+ // 確定檔案的大小
+ long fileSize = await GetFileSizeAsync(s3Client, bucketName, key);
+
+ using (var outputStream = new StreamWriter(outFile))
+ {
+ while (startPosition < fileSize)
+ {
+ long endPosition = Math.Min(startPosition + chunkSize, fileSize) - 1;
+
+ // 設定 ByteRange
+ var getObjectRequest = new GetObjectRequest
+ {
+ BucketName = bucketName,
+ Key = key,
+ ByteRange = new ByteRange(startPosition, endPosition)
+ };
+
+ using (var response = await s3Client.GetObjectAsync(getObjectRequest))
+ using (var reader = new StreamReader(response.ResponseStream))
+ {
+ // 處理部分檔案內容
+ var buffer = new char[chunkSize];
+ int bytesRead;
+ string line = string.Empty;
+ while ((bytesRead = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
+ {
+ line += new string(buffer, 0, bytesRead);
+ int newlineIndex = line.IndexOf('\n');
+ if (newlineIndex >= 0)
+ {
+ await outputStream.WriteAsync(line.Substring(0, newlineIndex + 1));
+ line = line.Substring(newlineIndex + 1);
+ }
+ }
+
+ // 處理剩餘的部分,如果有
+ if (!string.IsNullOrEmpty(line))
+ {
+ await outputStream.WriteLineAsync(line); // 添加換行符號
+ }
+ }
+
+ startPosition += chunkSize;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error: {ex.Message}");
+ }
+ }
+
+ private static async Task GetFileSizeAsync(IAmazonS3 s3Client, string bucketName, string objectKey)
+ {
+ var metadataRequest = new GetObjectMetadataRequest
+ {
+ BucketName = bucketName,
+ Key = objectKey
+ };
+
+ var response = await s3Client.GetObjectMetadataAsync(metadataRequest);
+ return response.ContentLength;
+ }
+
+ static async Task ReadObjectDataAsync(string filePath)
+ {
+ var s3Client = CreateS3Client();
+ var bucketName = "test-bucket";
+
+ var fileTransferUtilityRequest = new TransferUtilityUploadRequest()
+ {
+ };
+ var transferUtilityDownloadRequest = new TransferUtilityDownloadRequest
+ {
+ BucketName = null,
+ Key = null,
+ VersionId = null,
+ ModifiedSinceDateUtc = default,
+ UnmodifiedSinceDateUtc = default,
+ ChecksumMode = null,
+ ServerSideEncryptionCustomerProvidedKey = null,
+ ServerSideEncryptionCustomerProvidedKeyMD5 = null,
+ ServerSideEncryptionCustomerMethod = null,
+ FilePath = null
+ };
+ var getObjectRequest = new GetObjectRequest
+ {
+ BucketName = bucketName,
+ Key = filePath,
+
+ // PartNumber = 1,
+ ByteRange = new ByteRange(0, 14096),
+ };
+ var retryCount = 0;
+ while (true)
+ {
+ using var response = await s3Client.GetObjectAsync(getObjectRequest);
+ using var reader = new StreamReader(response.ResponseStream);
+ while (true)
+ {
+ var line = await reader.ReadLineAsync();
+ if (string.IsNullOrEmpty(line))
+ {
+ retryCount++;
+ break;
+ }
+
+ // Process the line
+ Console.WriteLine(line);
+ retryCount = 0;
+ }
+
+ if (retryCount >= 2)
+ {
+ break;
+ }
+ }
+ }
+
+ static async Task WriteObjectDataAsync(string filePath)
+ {
+ var s3Client = CreateS3Client();
+ var bucketName = "test-bucket";
+ var writerStream = new MemoryStream();
+ var writer = new StreamWriter(writerStream)
+ {
+ AutoFlush = true
+ };
+ try
+ {
+ await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+ using var reader = new StreamReader(fileStream);
+ var lineCount = 0;
+ var lineLimit = 10;
+ var putRequest = new PutObjectRequest()
+ {
+ BucketName = bucketName,
+ Key = filePath,
+ UseChunkEncoding = true,
+ AutoCloseStream = false,
+ AutoResetStreamPosition = true,
+ };
+ while (await reader.ReadLineAsync() is { } line)
+ {
+ // todo:可以處理一整批後再寫入到 s3
+ await writer.WriteLineAsync(line);
+ putRequest.InputStream = writerStream;
+ var response = await s3Client.PutObjectAsync(putRequest);
+ lineCount++;
+ }
+ }
+ catch (AmazonS3Exception e)
+ {
+ Console.WriteLine("Error encountered on server. Message:'{0}' when writing object", e.Message);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing object", e.Message);
+ }
+ }
+
+ static AmazonS3Client CreateS3Client(string url = "http://localhost:9000")
+ {
+ var credentials = new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE",
+ "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+
+ var s3Config = new AmazonS3Config
+ {
+ ServiceURL = url,
+ ForcePathStyle = true
+ };
+ return new AmazonS3Client(credentials, s3Config);
+ }
+}
\ 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.Aws.S3.MinIOS3/\344\270\212\345\202\263.csv" "b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/\344\270\212\345\202\263.csv"
new file mode 100644
index 00000000..728912cc
--- /dev/null
+++ "b/AWS/Lab.AwsS3/Lab.Aws.S3.MinIOS3/\344\270\212\345\202\263.csv"
@@ -0,0 +1,5 @@
+id,name,address,age
+1d290be4-9573-4e76-9f4c-4daa679caaad,Name1,Address1,45
+6dcd4ce0-df34-4e12-9ec8-2d1bf3be287c,Name2,Address2,20
+8bf2a7cb-2178-4f56-8653-67c58c2f8725,Name3,Address3,68
+8bf2a7cb-2178-4f56-8653-67c58c2f8725,Name4,Address3,68
diff --git a/AWS/Lab.AwsS3/Lab.AwsS3.sln b/AWS/Lab.AwsS3/Lab.AwsS3.sln
new file mode 100644
index 00000000..49cbf512
--- /dev/null
+++ b/AWS/Lab.AwsS3/Lab.AwsS3.sln
@@ -0,0 +1,22 @@
+
+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
+ .gitignore = .gitignore
+ 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
diff --git a/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/Web.config b/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/Web.config
index 89dc0452..ce4b0c60 100644
--- a/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/Web.config
+++ b/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/Web.config
@@ -5,64 +5,87 @@
-->
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/packages.config b/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/packages.config
index 59490826..8acef1aa 100644
--- a/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/packages.config
+++ b/AppMetrics/Lab.AppMetricToInfluxDB/WebApi.OWIN.NET48/packages.config
@@ -2,7 +2,7 @@
-
+
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole.sln b/Args/Lab.SpectreConsole/Lab.SpectreConsole.sln
new file mode 100644
index 00000000..d8e6c9a4
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SpectreConsole", "Lab.SpectreConsole\Lab.SpectreConsole.csproj", "{25BBDAD7-1286-4D7C-9AEA-948810AC146F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {25BBDAD7-1286-4D7C-9AEA-948810AC146F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25BBDAD7-1286-4D7C-9AEA-948810AC146F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25BBDAD7-1286-4D7C-9AEA-948810AC146F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25BBDAD7-1286-4D7C-9AEA-948810AC146F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole/AddSettings.cs b/Args/Lab.SpectreConsole/Lab.SpectreConsole/AddSettings.cs
new file mode 100644
index 00000000..d97e541e
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole/AddSettings.cs
@@ -0,0 +1,41 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace Lab.SpectreConsole;
+public class AddPackageCommand : Command
+{
+ public override int Execute(CommandContext context, AddPackageSettings settings)
+ {
+ // Omitted
+ return 0;
+ }
+}
+
+public class AddReferenceCommand : Command
+{
+ public override int Execute(CommandContext context, AddReferenceSettings settings)
+ {
+ // Omitted
+ return 0;
+ }
+}
+public class AddSettings : CommandSettings
+{
+ [CommandArgument(0, "[PROJECT]")]
+ public string Project { get; set; }
+}
+
+public class AddPackageSettings : AddSettings
+{
+ [CommandArgument(0, "")]
+ public string PackageName { get; set; }
+
+ [CommandOption("-v|--version ")]
+ public string Version { get; set; }
+}
+
+public class AddReferenceSettings : AddSettings
+{
+ [CommandArgument(0, "")]
+ public string ProjectReference { get; set; }
+}
\ No newline at end of file
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole/CancellableAsyncCommand.cs b/Args/Lab.SpectreConsole/Lab.SpectreConsole/CancellableAsyncCommand.cs
new file mode 100644
index 00000000..33740863
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole/CancellableAsyncCommand.cs
@@ -0,0 +1,72 @@
+using Microsoft.Extensions.Logging;
+using Spectre.Console.Cli;
+
+namespace Lab.SpectreConsole;
+
+public abstract class CancellableAsyncCommand : AsyncCommand
+ where TSettings : CommandSettings
+{
+ private readonly ILogger> _logger;
+
+ protected CancellableAsyncCommand(ILogger> logger)
+ {
+ this._logger = logger;
+ }
+
+ public abstract Task ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellation);
+
+ public override async Task ExecuteAsync(CommandContext context, TSettings settings)
+ {
+ using var cancellationSource = new CancellationTokenSource();
+
+ Console.CancelKeyPress += OnCancelKeyPress;
+ AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
+
+ using var _ = cancellationSource.Token.Register(
+ () =>
+ {
+ AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
+ Console.CancelKeyPress -= OnCancelKeyPress;
+ }
+ );
+ int exitCode = -1;
+ try
+ {
+ this._logger.LogInformation("執行任務中...");
+ var executeTask = this.ExecuteAsync(context, settings, cancellationSource.Token);
+ exitCode = await executeTask;
+ this._logger.LogInformation("執行完成!!!");
+ AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
+ Console.CancelKeyPress -= OnCancelKeyPress;
+ }
+ catch (OperationCanceledException)
+ {
+ exitCode = 0;
+ }
+ catch (Exception e)
+ {
+ this._logger.LogError(e, "執行命令時發生非預期的錯誤");
+ }
+
+ return exitCode;
+
+ void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e)
+ {
+ // NOTE: cancel event, don't terminate the process
+ e.Cancel = true;
+
+ cancellationSource.Cancel();
+ }
+
+ void OnProcessExit(object? sender, EventArgs e)
+ {
+ if (cancellationSource.IsCancellationRequested)
+ {
+ // NOTE: SIGINT (cancel key was pressed, this shouldn't ever actually hit however, as we remove the event handler upon cancellation of the `cancellationSource`)
+ return;
+ }
+
+ cancellationSource.Cancel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole/FileSizeAsyncCommand.cs b/Args/Lab.SpectreConsole/Lab.SpectreConsole/FileSizeAsyncCommand.cs
new file mode 100644
index 00000000..fafa5355
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole/FileSizeAsyncCommand.cs
@@ -0,0 +1,55 @@
+using System.ComponentModel;
+using Microsoft.Extensions.Logging;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace Lab.SpectreConsole;
+
+internal sealed class FileSizeAsyncCommand : CancellableAsyncCommand
+{
+ internal sealed class Settings : CommandSettings
+ {
+ [Description("Path to search. Defaults to current directory.")]
+ [CommandArgument(0, "[searchPath]")]
+ public string? SearchPath { get; init; }
+
+ [CommandOption("-p|--pattern")]
+ public string? SearchPattern { get; init; }
+
+ [CommandOption("--hidden")]
+ [DefaultValue(true)]
+ public bool IncludeHidden { get; init; }
+ }
+
+ public override async Task ExecuteAsync(CommandContext context,
+ Settings settings,
+ CancellationToken cancellation)
+ {
+ await Task.Delay(5000, cancellation);
+
+ var searchOptions = new EnumerationOptions
+ {
+ AttributesToSkip = settings.IncludeHidden
+ ? FileAttributes.Hidden | FileAttributes.System
+ : FileAttributes.System
+ };
+
+ var searchPattern = settings.SearchPattern ?? "*.*";
+ var searchPath = settings.SearchPath ?? Directory.GetCurrentDirectory();
+ var files = new DirectoryInfo(searchPath)
+ .GetFiles(searchPattern, searchOptions);
+
+ var totalFileSize = files
+ .Sum(fileInfo => fileInfo.Length);
+
+ AnsiConsole.MarkupLine(
+ $"Total file size for [green]{searchPattern}[/] files in [green]{searchPath}[/]: [blue]{totalFileSize:N0}[/] bytes");
+
+ return 0;
+ }
+
+ public FileSizeAsyncCommand(ILogger> logger)
+ : base(logger)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole/Lab.SpectreConsole.csproj b/Args/Lab.SpectreConsole/Lab.SpectreConsole/Lab.SpectreConsole.csproj
new file mode 100644
index 00000000..784b0165
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole/Lab.SpectreConsole.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+ app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Args/Lab.SpectreConsole/Lab.SpectreConsole/Program.cs b/Args/Lab.SpectreConsole/Lab.SpectreConsole/Program.cs
new file mode 100644
index 00000000..07291e12
--- /dev/null
+++ b/Args/Lab.SpectreConsole/Lab.SpectreConsole/Program.cs
@@ -0,0 +1,42 @@
+// See https://aka.ms/new-console-template for more information
+
+using Lab.SpectreConsole;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+using Serilog.Templates;
+using Spectre.Console.Extensions.Hosting;
+
+// var formatter = new CompactJsonFormatter();
+var formatter = new ExpressionTemplate(
+ "{ {_t: @t, _msg: @m, _props: @p} }\n");
+Log.Logger = new LoggerConfiguration()
+ // .MinimumLevel.Information()
+ .Enrich.FromLogContext()
+ .WriteTo.Console(formatter)
+ .WriteTo.File(formatter, "logs/host-.txt", rollingInterval: RollingInterval.Hour)
+ .CreateBootstrapLogger();
+
+var currentDomain = AppDomain.CurrentDomain;
+currentDomain.UnhandledException += (_, eventArgs) =>
+{
+ var e = (Exception)eventArgs.ExceptionObject;
+ Log.Error(e, "執行命令時發生非預期的錯誤");
+};
+
+try
+{
+ Log.Information("程序開始");
+ await Host.CreateDefaultBuilder(args)
+ .UseSerilog()
+ .UseConsoleLifetime()
+ .UseSpectreConsole(config => { config.AddCommand("filesize"); })
+ .RunConsoleAsync()
+ ;
+ Console.WriteLine("程序結束");
+ return Environment.ExitCode;
+}
+catch (Exception e)
+{
+ Log.Error(e, "執行命令時發生非預期的錯誤");
+ return -1;
+}
\ No newline at end of file
diff --git a/Args/Lab.SysCommand/Lab.SysCommand.sln b/Args/Lab.SysCommand/Lab.SysCommand.sln
new file mode 100644
index 00000000..70953a42
--- /dev/null
+++ b/Args/Lab.SysCommand/Lab.SysCommand.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.SysCommand", "Lab.SysCommand\Lab.SysCommand.csproj", "{FD82FD27-B9AF-4E34-8021-C5B3A1C34CC5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FD82FD27-B9AF-4E34-8021-C5B3A1C34CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FD82FD27-B9AF-4E34-8021-C5B3A1C34CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FD82FD27-B9AF-4E34-8021-C5B3A1C34CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FD82FD27-B9AF-4E34-8021-C5B3A1C34CC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Args/Lab.SysCommand/Lab.SysCommand/Lab.SysCommand.csproj b/Args/Lab.SysCommand/Lab.SysCommand/Lab.SysCommand.csproj
new file mode 100644
index 00000000..b66d7381
--- /dev/null
+++ b/Args/Lab.SysCommand/Lab.SysCommand/Lab.SysCommand.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+ app
+
+
+
+
+
+
+
+
diff --git a/Args/Lab.SysCommand/Lab.SysCommand/Program.cs b/Args/Lab.SysCommand/Lab.SysCommand/Program.cs
new file mode 100644
index 00000000..c7410ae9
--- /dev/null
+++ b/Args/Lab.SysCommand/Lab.SysCommand/Program.cs
@@ -0,0 +1,11 @@
+// See https://aka.ms/new-console-template for more information
+
+using System.CommandLine;
+
+var rootCommand = new RootCommand();
+var sub1Command = new Command("sub1", "First-level subcommand");
+rootCommand.Add(sub1Command);
+var sub1aCommand = new Command("sub1a", "Second level subcommand");
+sub1Command.Add(sub1aCommand);
+
+await rootCommand.InvokeAsync(args);
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..b08e38ae
--- /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..9527bcc9
--- /dev/null
+++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.UnitTest/OverrideResponseHandlerMiddlewareUnitTest.cs
@@ -0,0 +1,134 @@
+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"":""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);
+ }
+
+ [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 JsonSerializerOptions
+ {
+ 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..b6df6955
--- /dev/null
+++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse.csproj
@@ -0,0 +1,17 @@
+
+
+
+ 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..22de1636
--- /dev/null
+++ b/AspNetCore/Lib.Middleware.OverrideResponse/Lib.Middleware.OverrideResponse/OverrideResponseHandlerMiddleware.cs
@@ -0,0 +1,61 @@
+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 originalResponseBodyStream = context.Response.Body;
+ await using var newResponseBodyStream = new MemoryStream();
+ context.Response.Body = newResponseBodyStream;
+
+ await this._next(context);
+
+ newResponseBodyStream.Seek(0, SeekOrigin.Begin);
+ var statusCode = context.Response.StatusCode;
+ var fuzzyBody = statusCode switch
+ {
+ 401 => CreateFuzzyBody("NoAuthentication"),
+ 403 => CreateFuzzyBody("NoAuthorization"),
+ _ => null
+ };
+
+ if (fuzzyBody != null)
+ {
+ 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(fuzzyData);
+ }
+ else
+ {
+ await newResponseBodyStream.CopyToAsync(originalResponseBodyStream);
+ context.Response.Body = originalResponseBodyStream;
+ }
+ }
+
+ private static object CreateFuzzyBody(string failureCode)
+ {
+ return new
+ {
+ ErrorCode = failureCode,
+ ErrorMessage = "Please contact your administrator"
+ };
+ }
+}
\ No newline at end of file
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..a926f283
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Controllers/ValuesController.cs
@@ -0,0 +1,79 @@
+using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using StackExchange.Profiling;
+
+namespace Lab.NETMiniProfiler.ASPNetCore5.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 = this._employeeDbContextFactory.CreateDbContext();
+ 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 = 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(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.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj
new file mode 100644
index 00000000..98d92d65
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Lab.NETMiniProfiler.ASPNetCore5.csproj
@@ -0,0 +1,26 @@
+
+
+
+ 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..2c7f3c3c
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Properties/launchSettings.json
@@ -0,0 +1,36 @@
+{
+ "$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",
+ "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",
+ "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
new file mode 100644
index 00000000..8d925b6b
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore5/Startup.cs
@@ -0,0 +1,79 @@
+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 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", "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.UseMiniProfiler();
+ }
+
+ PreConnectionDb(app);
+
+ 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 = "WebApplication1", Version = "v1" });
+ });
+
+ services.AddMiniProfiler(o => o.RouteBasePath = "/profiler")
+ .AddEntityFramework();
+ services.AddAppEnvironment();
+ services.AddEntityFramework();
+ }
+
+ private 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.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.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..de1b6b14
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Lab.NETMiniProfiler.ASPNetCore6.csproj
@@ -0,0 +1,24 @@
+
+
+
+ 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..add4dbc4
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.ASPNetCore6/Program.cs
@@ -0,0 +1,55 @@
+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);
+
+// 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();
+PreConnectionDb(app);
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ // 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();
+}
+
+app.UseHttpsRedirection();
+
+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/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.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)
+
+
+
+
+
+
+
+
+
+
+
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..e42e5a84
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppDependencyInjectionExtensions.cs
@@ -0,0 +1,65 @@
+using Lab.NETMiniProfiler.Infrastructure.EFCore5.EntityModel;
+using Microsoft.Data.SqlClient;
+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;
+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, optionsBuilder) =>
+ {
+ // var mssqlOptions = optionsBuilder.Options.FindExtension();
+ // var npgsqlOptions = optionsBuilder.Options.FindExtension();
+
+ var appOption = provider.GetService();
+ var loggerFactory = provider.GetService();
+ 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" }))
+
+ // .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
new file mode 100644
index 00000000..4f5eca78
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/AppEnvironmentOption.cs
@@ -0,0 +1,53 @@
+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
+ {
+ 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 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
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..a98e18d5
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/EntityModel/EmployeeDbContext.cs
@@ -0,0 +1,77 @@
+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}'");
+ }
+
+ if (this.Database.CanConnect() == false)
+ {
+ this.Database.EnsureCreated();
+ }
+ else
+ {
+ 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..8f5315eb
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore5/Lab.NETMiniProfiler.Infrastructure.EFCore5.csproj
@@ -0,0 +1,18 @@
+
+
+
+ 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..7230b81e
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppDependencyInjectionExtensions.cs
@@ -0,0 +1,49 @@
+using Lab.NETMiniProfiler.Infrastructure.EFCore6.EntityModel;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Lab.NETMiniProfiler.Infrastructure.EFCore6;
+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 appOption = provider.GetService();
+ var loggerFactory = provider.GetService();
+ 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" }))
+
+ // .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
new file mode 100644
index 00000000..6614f3be
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/AppEnvironmentOption.cs
@@ -0,0 +1,53 @@
+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
+ {
+ 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 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
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..1799f47d
--- /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.EFCore6.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..14beaacf
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EntityModel/EmployeeDbContext.cs
@@ -0,0 +1,77 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
+
+namespace Lab.NETMiniProfiler.Infrastructure.EFCore6.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}'");
+ }
+
+ if (this.Database.CanConnect() == false)
+ {
+ this.Database.EnsureCreated();
+ }
+ else
+ {
+ 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..47f791e7
--- /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.EFCore6.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..f582242b
--- /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.EFCore6.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..bc7c8f51
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/EnvironmentAssistant.cs
@@ -0,0 +1,15 @@
+namespace Lab.NETMiniProfiler.Infrastructure.EFCore6;
+
+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..2af7dc9f
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.Infrastructure.EFCore6/Lab.NETMiniProfiler.Infrastructure.EFCore6.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 10
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln
new file mode 100644
index 00000000..5f6fca96
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/Lab.NETMiniProfiler.sln
@@ -0,0 +1,56 @@
+
+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
+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
+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
+ 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
+ {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
+ {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
+ EndGlobalSection
+ 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}
+ {A677D49D-8088-44DD-9900-FC7694C30D70} = {8E1AD56E-E673-4533-B933-3712BD42BD4D}
+ {D4CB746B-5711-4338-B716-763A86822134} = {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..80c43a60
--- /dev/null
+++ b/Benchmark/Lab.NETMiniProfiler/docker-compose.yml
@@ -0,0 +1,17 @@
+version: "3.8"
+
+services:
+ db-mssql:
+ image: mcr.microsoft.com/mssql/server:2019-latest
+ environment:
+ - ACCEPT_EULA=Y
+ - SA_PASSWORD=pass@w0rd1~
+ ports:
+ - 1433:1433
+
+ db-postgres:
+ image: postgres:12-alpine
+ environment:
+ - POSTGRES_PASSWORD=guest
+ ports:
+ - 5432:5432
\ No newline at end of file
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 @@
-
+
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
diff --git a/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj
new file mode 100644
index 00000000..67c4441e
--- /dev/null
+++ b/Configuration/Lab.ConfigBind/Lab.ConfigBind.TestProject/Lab.ConfigBind.TestProject.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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"
new file mode 100644
index 00000000..0565a7fe
--- /dev/null
+++ "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"
@@ -0,0 +1,98 @@
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+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.AddEnvironmentVariables().Build();
+ var member = configRoot.Get>();
+
+ Assert.AreEqual("9527", member["a"].Id);
+ Assert.AreEqual("9528", member["b"].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();
+ 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
+ {
+ ["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);
+ }
+
+ 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..a9c53ad4
--- /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,90 @@
+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("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();
+ var configRoot = builder.AddEnvironmentVariables()
+ .Build();
+ var member = configRoot.Get>();
+
+ Assert.AreEqual("9527", member["a"].Id);
+ Assert.AreEqual("9528", member["b"].Id);
+ }
+
+ [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();
+ var configRoot = builder.AddEnvironmentVariables()
+ .Build();
+ var member = 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();
+ var configRoot = builder.AddEnvironmentVariables()
+ .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,
+ 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.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.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..27513906
--- /dev/null
+++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig.TestProject/UnitTest1.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Lab.EnvFileConfig.TestProject;
+
+[TestClass]
+public class UnitTest1
+{
+ [TestMethod]
+ public void 讀取ENV檔案()
+ {
+ var configRoot = new ConfigurationBuilder()
+
+ // .AddJsonFile("appSettings.json")
+ .AddEnvFile("secret.env")
+ .Build()
+ ;
+ 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.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/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/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..48c93535
--- /dev/null
+++ b/Configuration/Lab.EnvFileConfig/Lab.EnvFileConfig/Lab.EnvFileConfig.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/Configuration/Lab.EnvGenerator/Lab.EnvGenerator.sln b/Configuration/Lab.EnvGenerator/Lab.EnvGenerator.sln
new file mode 100644
index 00000000..fb45d981
--- /dev/null
+++ b/Configuration/Lab.EnvGenerator/Lab.EnvGenerator.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab.EnvGeneratorCli", "Lab.EnvGeneratorCli\Lab.EnvGeneratorCli.csproj", "{FF0E7D63-9FCA-4062-9DE2-1BF6A92A7100}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FF0E7D63-9FCA-4062-9DE2-1BF6A92A7100}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FF0E7D63-9FCA-4062-9DE2-1BF6A92A7100}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FF0E7D63-9FCA-4062-9DE2-1BF6A92A7100}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FF0E7D63-9FCA-4062-9DE2-1BF6A92A7100}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/ConvertEnvCommand.cs b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/ConvertEnvCommand.cs
new file mode 100644
index 00000000..d9b26b29
--- /dev/null
+++ b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/ConvertEnvCommand.cs
@@ -0,0 +1,86 @@
+using Spectre.Console.Cli;
+
+namespace Lab.EnvGeneratorCli;
+
+internal sealed class ConvertEnvCommand : Command
+{
+ public sealed class Settings : CommandSettings
+ {
+ [CommandOption("--env")]
+ public string? Environment { get; init; }
+ }
+
+ public override int Execute(CommandContext context, Settings settings)
+ {
+ // 讀取 env.template 檔案
+ var envTemplate = File.ReadAllLines("env.template");
+
+ var env = settings.Environment;
+
+ var outputFileName = $"app.{env}.env";
+
+ // 解析 env.template 檔案
+ var contents = ParseEnvTemplate(envTemplate, env);
+ GenerateEnvFile(contents, outputFileName);
+ Console.WriteLine($"Generated {outputFileName}.");
+
+ return 0;
+ }
+
+ private static void GenerateEnvFile(Dictionary settings, string outputFileName)
+ {
+ using var writer = new StreamWriter(outputFileName);
+ foreach (var setting in settings)
+ {
+ writer.WriteLine($"{setting.Key}={setting.Value}");
+ }
+ }
+
+ private static Dictionary ParseEnvTemplate(string[] templateLines, string env)
+ {
+ var result = new Dictionary();
+ var currentSection = "";
+ foreach (var line in templateLines)
+ {
+ if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
+ {
+ continue;
+ }
+
+ // 找出 section
+ if (line.StartsWith("[") && line.EndsWith("]"))
+ {
+ currentSection = line.Trim('[', ']');
+ continue;
+ }
+
+ var parts = line.Split('=', StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length < 2)
+ {
+ continue;
+ }
+
+ var key = parts[0].Trim();
+ var value = parts[1].Trim();
+ if (parts.Length > 2)
+ {
+ // 被分割的部分重新組合
+ value = string.Join("=", parts.Skip(1)).Trim();
+ }
+
+ // 優先取環境變數的值
+ if (key == env)
+ {
+ result[currentSection] = value;
+ }
+
+ // 若沒有環境變數的值,則取 default
+ else if (key == "default")
+ {
+ result.TryAdd(currentSection, value);
+ }
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Lab.EnvGeneratorCli.csproj b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Lab.EnvGeneratorCli.csproj
new file mode 100644
index 00000000..215ad69f
--- /dev/null
+++ b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Lab.EnvGeneratorCli.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ EnvGeneratorCli
+ EnvGeneratorCli
+ cli
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Program.cs b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Program.cs
new file mode 100644
index 00000000..884fba54
--- /dev/null
+++ b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/Program.cs
@@ -0,0 +1,19 @@
+using Spectre.Console.Cli;
+
+namespace Lab.EnvGeneratorCli;
+
+internal class Program
+{
+ private static void Main(string[] args)
+ {
+ var app = new CommandApp();
+ app.Configure(config =>
+ {
+ config.AddCommand("convert")
+ .WithDescription("convert a file.")
+ .WithExample("convert", "--env", "qa");
+ });
+
+ app.Run(args);
+ }
+}
\ No newline at end of file
diff --git a/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/env.template b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/env.template
new file mode 100644
index 00000000..2cae63d3
--- /dev/null
+++ b/Configuration/Lab.EnvGenerator/Lab.EnvGeneratorCli/env.template
@@ -0,0 +1,12 @@
+[DefaultConnection]
+default=local
+qa=Server=qa;Database=proddb;User Id=produser;Password=prodpassword;
+prod=Server=prodserver;Database=proddb;User Id=produser;Password=prodpassword;
+
+[Ports]
+default=8001, 8001, 8002
+
+[Allowed]
+default=false
+qa=true
+prod=false
\ No newline at end of file
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..1fd4ffcf
--- /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..291bd670
--- /dev/null
+++ b/Configuration/Lab.Environment/Lab.Environment.sln
@@ -0,0 +1,27 @@
+
+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
+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
+ 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..b97d93b8
--- /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
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj b/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj
index dceedada..24827e37 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/AspNetCore3.csproj
@@ -1,22 +1,29 @@
-
- netcoreapp3.1
-
+
+ netcoreapp3.1
+ Debug;Release;QA
+ AnyCPU
+
-
-
-
+
+ true
+ false
+
-
-
-
+
+
+
-
-
- Always
-
-
+
+
+
+
+
+
+ Always
+
+
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..170d017e
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/DefaultController.cs
@@ -0,0 +1,38 @@
+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
+ {
+ [Route("options/appsettings")]
+ public IActionResult Get()
+ {
+ var serviceProvider = this.HttpContext.RequestServices;
+ var options = serviceProvider.GetService>();
+ return this.Ok(options?.Value);
+ }
+
+ [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>();
+ var player = playerOption.Get($"Player{id}");
+ return this.Ok(player);
+ }
+ }
+}
\ 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..55424a39 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Controllers/WeatherForecastController.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Lab.Infra;
@@ -19,32 +19,32 @@ 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)
- {
- 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)
//{
@@ -83,9 +85,9 @@ public IEnumerable Get()
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 c9edcdf5..242d45b9 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Program.cs
@@ -1,29 +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.UseStartup();
- //webBuilder.UseStartup();
- //webBuilder.UseStartup();
- webBuilder.UseStartup();
- });
}
-}
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json
index 886261e3..546f5d39 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/Properties/launchSettings.json
@@ -13,6 +13,7 @@
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
+ "//launchUrl": "default",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs
index 1134ef0e..a1adff51 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/ServiceCollectionEx.cs
@@ -1,35 +1,36 @@
-using System;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
+// 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;
+// }
+// }
+// }
-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/AspNetCore3/Startup.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/Startup.cs
index d4ba7974..d4468799 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.
+
+ //注入 Options 和完整 IConfiguration
+ services.Configure(this.Configuration);
+
+ //注入 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"));
+ // services.Configure("ConnectionStrings", this.Configuration.GetSection("ConnectionStrings"));
}
}
}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIAppSetting.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs
similarity index 73%
rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIAppSetting.cs
rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionAppSetting.cs
index 59fd68f8..311eda8e 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;
}
@@ -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);
+ //注入 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
new file mode 100644
index 00000000..5a43db3c
--- /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();
+
+ //注入 Options 和完整 IConfiguration
+ services.Configure(this.Configuration);
+
+ //注入 Options 和 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/Startup_InjectionIOptions.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs
similarity index 84%
rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptions.cs
rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptions.cs
index 716c5e74..f953b4ce 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;
}
@@ -38,10 +38,7 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
- //`J IOptions
- services.AddOptions();
-
- //`J IConfiguration
+ //注入 Options 和完整 IConfiguration
services.Configure(this.Configuration);
}
}
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs
similarity index 78%
rename from Configuration/NetCore/Lab.Config/AspNetCore3/Startup_InjectionIOptionsSnapshot.cs
rename to Configuration/NetCore/Lab.Config/AspNetCore3/StartupInjectionOptionsSnapshot.cs
index ee727426..7d983108 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;
}
@@ -38,28 +38,27 @@ 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
+ //注入 Options 和完整 IConfiguration
+ services.Configure(this.Configuration);
+ //注入 Options 和 Configuration Section Name
services.Configure(this.Configuration);
services.Configure("Player1", this.Configuration.GetSection("Player1"));
services.Configure("Player2", this.Configuration.GetSection("Player2"));
-
- //services.AddSingleton(Configuration);
+ 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 39d2dbef..c03edfe4 100644
--- a/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json
+++ b/Configuration/NetCore/Lab.Config/AspNetCore3/appsettings.json
@@ -6,22 +6,25 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
- "AllowedHosts": "",
-
+ "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": "testApp",
- "Key": "12345678990"
+ "AppId": "player",
+ "Key": "1234567890"
},
-
"Player1": {
- "AppId": "testApp",
+ "AppId": "player1",
"Key": "12345678990"
},
"Player2": {
- "AppId": "testApp",
- "Key": "12345678990"
+ "AppId": "player2",
+ "Key": "player2_123456"
+ },
+ "Player3": {
+ "AppId": "player3",
+ "Key": "player3_123456"
}
-}
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj
new file mode 100644
index 00000000..92063f64
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore5/AspNetCore5.csproj
@@ -0,0 +1,19 @@
+
+
+
+ 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..9e817d64
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Controllers/DefaultController.cs
@@ -0,0 +1,68 @@
+using System;
+using Lab.Infra;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+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("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)
+ {
+ var serviceProvider = this.HttpContext.RequestServices;
+ var appSettingOptions = serviceProvider.GetService>();
+ var playerOptions = serviceProvider.GetService>();
+ var content = new
+ {
+ App = appSettingOptions?.CurrentValue,
+ Player = playerOptions?.Get($"Player{id}")
+ };
+ appSettingOptions.OnChange(p =>
+ {
+ Console.WriteLine("節點已變更");
+ });
+ 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);
+ }
+ }
+}
\ 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..dab91d80
--- /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..1fc668cf
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Program.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+
+namespace AspNetCore5
+{
+ 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();
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+ }
+}
\ 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..78d69940
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore5/Startup.cs
@@ -0,0 +1,79 @@
+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.
+
+ //注入 Options 和完整 IConfiguration
+ services.Configure(this.Configuration);
+
+ //注入 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"));
+ 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/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..25a5a48f
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/AspNetCore5/appsettings.json
@@ -0,0 +1,30 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ "ConnectionStrings": {
+ "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;",
+ "AuthenticationConnectionString": ""
+ },
+ "Player": {
+ "AppId": "player23",
+ "Key": "1234567890"
+ },
+ "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 d3dc3987..11296a46 100644
--- a/Configuration/NetCore/Lab.Config/Lab.Config.sln
+++ b/Configuration/NetCore/Lab.Config/Lab.Config.sln
@@ -9,24 +9,47 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsUnitTest", "MsUnitTest\Ms
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore3", "AspNetCore3\AspNetCore3.csproj", "{2362CB3D-B69D-4C2D-B873-685F81140D1B}"
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
Release|Any CPU = Release|Any CPU
+ QA|Any CPU = QA|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{438294F7-7612-4190-A769-EFA5F34118DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{438294F7-7612-4190-A769-EFA5F34118DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{438294F7-7612-4190-A769-EFA5F34118DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{438294F7-7612-4190-A769-EFA5F34118DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {438294F7-7612-4190-A769-EFA5F34118DD}.QA|Any CPU.ActiveCfg = QA|Any CPU
+ {438294F7-7612-4190-A769-EFA5F34118DD}.QA|Any CPU.Build.0 = QA|Any CPU
{623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.QA|Any CPU.ActiveCfg = QA|Any CPU
+ {623F3B3A-4C40-40C7-94C1-DBD7CD928CC4}.QA|Any CPU.Build.0 = QA|Any CPU
{2362CB3D-B69D-4C2D-B873-685F81140D1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2362CB3D-B69D-4C2D-B873-685F81140D1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2362CB3D-B69D-4C2D-B873-685F81140D1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2362CB3D-B69D-4C2D-B873-685F81140D1B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2362CB3D-B69D-4C2D-B873-685F81140D1B}.QA|Any CPU.ActiveCfg = QA|Any CPU
+ {2362CB3D-B69D-4C2D-B873-685F81140D1B}.QA|Any CPU.Build.0 = QA|Any CPU
+ {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D0B23D3B-F2E0-4B38-8864-F3FF60EE115B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {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
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 792466b1..fc35969d 100644
--- a/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj
+++ b/Configuration/NetCore/Lab.Config/Lab.Infra/Lab.Infra.csproj
@@ -1,11 +1,18 @@
-
- netcoreapp3.1
-
+
+ netcoreapp3.1
+ Debug;Release;QA
+ AnyCPU
+
-
-
-
+
+ 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 a32946c2..e3c2748e 100644
--- a/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj
+++ b/Configuration/NetCore/Lab.Config/MsUnitTest/MsUnitTest.csproj
@@ -1,28 +1,37 @@
-
- netcoreapp3.1
-
- 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/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/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
new file mode 100644
index 00000000..8fa36c8b
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOption.cs
@@ -0,0 +1,30 @@
+using System;
+using Microsoft.Extensions.Options;
+
+namespace NetFx48
+{
+ public class AppWorkFlowWithOption : IAppWorkFlow
+ {
+ private readonly AppSetting1 _appSetting;
+
+ public AppWorkFlowWithOption(IOptions options)
+ {
+ try
+ {
+ this._appSetting = options.Value;
+ }
+ catch (OptionsValidationException ex)
+ {
+ foreach (var failure in ex.Failures)
+ {
+ Console.WriteLine(failure);
+ }
+ }
+ }
+
+ public string GetPlayerId()
+ {
+ return this._appSetting.Player.AppId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs
new file mode 100644
index 00000000..97fe82cb
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsMonitor.cs
@@ -0,0 +1,26 @@
+using System;
+using Microsoft.Extensions.Options;
+
+namespace NetFx48
+{
+ public class AppWorkFlowWithOptionsMonitor : IAppWorkFlow
+ {
+ private readonly AppSetting1 _appSetting;
+ private readonly Player1 _player;
+
+ public AppWorkFlowWithOptionsMonitor(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/AppWorkFlowWithOptionsSnapshot.cs b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsSnapshot.cs
new file mode 100644
index 00000000..72a1f7c6
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/AppWorkFlowWithOptionsSnapshot.cs
@@ -0,0 +1,26 @@
+using System;
+using Microsoft.Extensions.Options;
+
+namespace NetFx48
+{
+ public class AppWorkFlowWithOptionsSnapshot : IAppWorkFlow
+ {
+ private readonly AppSetting1 _appSetting;
+ private readonly Player1 _player;
+
+ public AppWorkFlowWithOptionsSnapshot(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/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/Models/AppSetting.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs
new file mode 100644
index 00000000..21ad3ba8
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/Models/AppSetting.cs
@@ -0,0 +1,17 @@
+namespace NetFx48
+{
+ public struct AppSetting
+ {
+ public Player Player { get; set; }
+
+ 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/Models/ConnectionStrings.cs b/Configuration/NetCore/Lab.Config/NetFx48/Models/ConnectionStrings.cs
new file mode 100644
index 00000000..b4baf941
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/Models/ConnectionStrings.cs
@@ -0,0 +1,9 @@
+namespace NetFx48
+{
+ public struct ConnectionStrings
+ {
+ public string DefaultConnectionString { get; set; }
+
+ public string AuthenticationConnectionString { get; set; }
+ }
+}
\ 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
new file mode 100644
index 00000000..7b778d7d
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/Models/Player.cs
@@ -0,0 +1,16 @@
+namespace NetFx48
+{
+ public struct Player
+ {
+ public string AppId { get; set; }
+
+ 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/NetFx48.csproj b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj
new file mode 100644
index 00000000..92ceb1d4
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/NetFx48.csproj
@@ -0,0 +1,65 @@
+
+
+
+ net48
+
+ false
+
+ latest
+
+ Debug;Release;QA
+
+ AnyCPU
+ 659be13b-676e-4c9e-a0b9-0df2ffd75cfc
+
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ Always
+ PreserveNewest
+
+
+ true
+ Always
+ PreserveNewest
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
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/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs
new file mode 100644
index 00000000..6e1eaddb
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyCommandConfigurationTests.cs
@@ -0,0 +1,80 @@
+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;
+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);
+ provider.TryGet("Config", out var configPath);
+ Console.WriteLine($"{args.First()}\r\n" +
+ $"AppId:{appId}\r\n" +
+ $"ConfigPath:{configPath}");
+ }
+
+ [TestMethod]
+ [DataRow(new[] {"-i=1234567890", "-c=app.json"})]
+ public void 命令對應_Host(string[] args)
+ {
+ var map = new Dictionary
+ {
+ {"-i", "AppId"},
+ {"-c", "Config"}
+ };
+ var builder = Host.CreateDefaultBuilder()
+ .ConfigureAppConfiguration(config =>
+ {
+ // config.Sources.Clear();
+ config.AddCommandLine(args, map);
+ var configRoot = config.Build();
+
+ 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(AppWorkFlow));
+ })
+ ;
+ 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
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs
new file mode 100644
index 00000000..aed350b8
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyEnvironmentVariablesConfigurationTests.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+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 SurveyEnvironmentVariablesConfigurationTests
+ {
+ [TestMethod]
+ 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"]}");
+ })
+ ;
+ var host = builder.Build();
+ var environment = host.Services.GetRequiredService();
+ Console.WriteLine($"EnvironmentName={environment.EnvironmentName}");
+ }
+
+ [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"]}");
+ }
+
+ [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}");
+ }
+
+ [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
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyIniConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyIniConfigurationTests.cs
new file mode 100644
index 00000000..aa2f3c96
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyIniConfigurationTests.cs
@@ -0,0 +1,27 @@
+using System;
+using System.IO;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace NetFx48
+{
+ [TestClass]
+ public class SurveyIniConfigurationTests
+ {
+ [TestMethod]
+ public void 手動實例化ConfigurationBuilder()
+ {
+ var configBuilder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddIniFile("appsettings.ini", optional: false, reloadOnChange: 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"]}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs
new file mode 100644
index 00000000..8118d15a
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyJsonConfigurationTests.cs
@@ -0,0 +1,198 @@
+using System;
+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;
+
+namespace NetFx48
+{
+ [TestClass]
+ public class SurveyJsonConfigurationTests
+ {
+ [TestMethod]
+ public void 切換組態()
+ {
+ string environmentName;
+#if DEBUG
+ environmentName = "Development";
+#elif QA
+ environmentName = "QA";
+#elif STAGING
+ environmentName = "Staging";
+#elif RELEASE
+ environmentName = "Production";
+#endif
+ var configBuilder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", false, true)
+ .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"]}");
+ }
+
+ [TestMethod]
+ public void 手動實例化ConfigurationBuilder()
+ {
+ var configBuilder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", true, 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 注入Configuration()
+ {
+ var builder = Host.CreateDefaultBuilder(null)
+ .ConfigureAppConfiguration(config =>
+ {
+ config.Sources.Clear();
+ config.AddJsonFile("appsettings.json", true, true);
+ })
+ .ConfigureServices(service =>
+ {
+ //DI
+ service.AddScoped(typeof(AppWorkFlow));
+ });
+ var host = builder.Build();
+
+ var appService = host.Services.GetService();
+ var playerId = appService.GetPlayerId();
+ Console.WriteLine($"AppId = {playerId}");
+ }
+
+ [TestMethod]
+ 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 讀取設定檔_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]
+ 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()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json");
+ var configRoot = builder.Build();
+
+ //TryGet
+ foreach (var provider in configRoot.Providers)
+ {
+ provider.TryGet("Player:AppId", out var value);
+ Console.WriteLine($"AppId = {value}");
+ }
+ }
+
+ [TestMethod]
+ public void 讀取設定檔_綁定()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json");
+ var configRoot = builder.Build();
+
+ var appSetting = new AppSetting();
+ configRoot.Bind(appSetting);
+ Console.WriteLine($"AppId = {appSetting.Player.AppId}");
+ Console.WriteLine($"Key = {appSetting.Player.Key}");
+ Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}");
+ }
+
+ [TestMethod]
+ public void 讀取設定檔_綁定_Get()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json");
+ 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}");
+ Console.WriteLine($"Connection String = {appSetting.ConnectionStrings.DefaultConnectionString}");
+ }
+
+ 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"]}");
+ })
+ .ConfigureServices(service =>
+ {
+ //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
new file mode 100644
index 00000000..573309f1
--- /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 configRoot = configBuilder.Build();
+
+ //讀取組態
+ var actual = configRoot["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/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
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs
new file mode 100644
index 00000000..96824af4
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyOptionTests.cs
@@ -0,0 +1,180 @@
+using System;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace NetFx48
+{
+ [TestClass]
+ public class SurveyOptionTests
+ {
+ [TestMethod]
+ public void 注入Option()
+ {
+ var builder = Host.CreateDefaultBuilder()
+ .ConfigureAppConfiguration((hosting, configBuilder) =>
+ {
+ // 1.讀組態檔
+ var environmentName =
+ hosting.Configuration["ENVIRONMENT2"];
+ configBuilder.AddJsonFile("appsettings.json", false, true);
+ configBuilder
+ .AddJsonFile($"appsettings.{environmentName}.json",
+ true, true);
+ })
+ .ConfigureServices((hosting, services) =>
+ {
+ // 2. 注入 Option 和 Configuration
+ services.Configure(hosting.Configuration);
+
+ //注入其他服務
+ services.AddSingleton();
+ })
+ ;
+ var host = builder.Build();
+ var service = host.Services.GetService();
+ var playerId = service.GetPlayerId();
+ Console.WriteLine($"PlayerId = {playerId}");
+ }
+
+ [TestMethod]
+ public void 注入OptionMonitor()
+ {
+ var builder = Host.CreateDefaultBuilder()
+ .ConfigureAppConfiguration((hosting, configBuilder) =>
+ {
+ // 1.讀組態檔
+ var environmentName =
+ hosting.Configuration["ENVIRONMENT2"];
+ configBuilder.AddJsonFile("appsettings.json", false, true);
+ configBuilder
+ .AddJsonFile($"appsettings.{environmentName}.json",
+ true, true);
+ })
+ .ConfigureServices((hosting, services) =>
+ {
+ // 注入 Option 和完整 Configuration
+ services.Configure(hosting.Configuration);
+
+ // 注入 Option 和特定 Configuration Section Name
+ services.Configure("Player",
+ hosting.Configuration.GetSection("Player"));
+
+ //注入其他服務
+ services.AddScoped();
+ })
+ ;
+ var host = builder.Build();
+ var service = host.Services.GetService();
+ var playerId = service.GetPlayerId();
+ Console.WriteLine($"PlayerId = {playerId}");
+ }
+
+ [TestMethod]
+ public void 注入OptionSnapshot()
+ {
+ 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) =>
+ {
+ // 注入 Option by 完整組態
+ services.Configure(hosting.Configuration);
+
+ // 注入 Option by 特定組態
+ services.Configure(hosting.Configuration
+ .GetSection("Player"));
+
+ //注入其他服務
+ services.AddScoped();
+ })
+ ;
+ var host = builder.Build();
+ var service = host.Services.GetService();
+ var playerId = service.GetPlayerId();
+ Console.WriteLine($"PlayerId = {playerId}");
+ }
+
+[TestMethod]
+ public void 驗證()
+ {
+ var builder = Host.CreateDefaultBuilder()
+ .ConfigureAppConfiguration((hosting, configBuilder) =>
+ {
+ // 1.讀組態檔
+ var environmentName =
+ hosting.Configuration["ENVIRONMENT2"];
+ configBuilder.AddJsonFile("appsettings.json", false, true);
+ configBuilder
+ .AddJsonFile($"appsettings.{environmentName}.json",
+ true, true);
+ })
+ .ConfigureServices((hosting, services) =>
+ {
+ // 2. 注入 Option 和 Configuration
+ services.Configure(hosting.Configuration);
+ //驗證
+ services.AddOptions()
+ .ValidateDataAnnotations()
+ .Validate(p =>
+ {
+ var hasContent = string.IsNullOrWhiteSpace(p.ConnectionStrings.DefaultConnectionString);
+ if (hasContent == false)
+ {
+ return false;
+ }
+
+ return true;
+ },
+ "DefaultConnectionString must be value"); // Failure message.
+ ;
+
+ //注入其他服務
+ services.AddSingleton();
+ })
+ ;
+ var host = builder.Build();
+ var service = host.Services.GetService();
+ var playerId = service.GetPlayerId();
+ Console.WriteLine($"PlayerId = {playerId}");
+ }
+
+ [TestMethod]
+ public void 直接注入組態物件()
+ {
+ var builder = Host.CreateDefaultBuilder()
+ .ConfigureAppConfiguration((hosting, configBuilder) =>
+ {
+ // 1.讀組態檔
+ 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);
+
+ //注入其他服務
+ 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
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyUserSecretTests.cs
new file mode 100644
index 00000000..32043141
--- /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讀取秘密()
+ {
+ 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 手動實例化組態讀取秘密()
+ {
+ 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/SurveyXmlConfigurationTests.cs b/Configuration/NetCore/Lab.Config/NetFx48/SurveyXmlConfigurationTests.cs
new file mode 100644
index 00000000..15bf423b
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/SurveyXmlConfigurationTests.cs
@@ -0,0 +1,26 @@
+using System;
+using System.IO;
+using Microsoft.Extensions.Configuration;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace NetFx48
+{
+ [TestClass]
+ public class SurveyXmlConfigurationTests
+ {
+ [TestMethod]
+ public void 手動實例化ConfigurationBuilder()
+ {
+ var configBuilder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddXmlFile("appsettings.xml", false, 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"]}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json
new file mode 100644
index 00000000..b12c223d
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.QA.json
@@ -0,0 +1,9 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ConsoleApp.NewDb.QA;Trusted_Connection=True;"
+ },
+ "Player": {
+ "AppId": "qa",
+ "Key": "qa1234567890"
+ }
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini
new file mode 100644
index 00000000..2b9d12b9
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.ini
@@ -0,0 +1,6 @@
+[ConnectionStrings]
+DefaultConnectionString = "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
+
+[Player]
+AppId = testApp
+Key = 12345678990
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json
new file mode 100644
index 00000000..20f51da4
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
+ },
+ "Player": {
+ "AppId": "player1",
+ "Key": "1234567890"
+ },
+ "Environment": "Development",
+ "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
new file mode 100644
index 00000000..420fcb73
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.test.json
@@ -0,0 +1,9 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb.Test;Trusted_Connection=True;"
+ },
+ "Player": {
+ "AppId": "test",
+ "Key": "test1234567890"
+ }
+}
\ No newline at end of file
diff --git a/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml
new file mode 100644
index 00000000..80f4425b
--- /dev/null
+++ b/Configuration/NetCore/Lab.Config/NetFx48/appsettings.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Server=(localdb)\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;
+
+
+
+ testApp
+ 12345678990
+
+
\ 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
diff --git a/Host/ConsoleAppNet5/ConsoleAppNet5.csproj b/Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj
similarity index 100%
rename from Host/ConsoleAppNet5/ConsoleAppNet5.csproj
rename to Coravel/Lab.CoravelScheduler/ConsoleApp1/ConsoleApp1.csproj
diff --git a/Host/ConsoleAppNet5/Program.cs b/Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs
similarity index 86%
rename from Host/ConsoleAppNet5/Program.cs
rename to Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs
index c6aa3ecd..be1b5acd 100644
--- a/Host/ConsoleAppNet5/Program.cs
+++ b/Coravel/Lab.CoravelScheduler/ConsoleApp1/Program.cs
@@ -1,6 +1,6 @@
using System;
-namespace ConsoleAppNet5
+namespace ConsoleApp1
{
class Program
{
diff --git a/Coravel/Lab.CoravelScheduler/Lab.CoravelScheduler.sln b/Coravel/Lab.CoravelScheduler/Lab.CoravelScheduler.sln
new file mode 100644
index 00000000..cc4f87b3
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/Lab.CoravelScheduler.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30804.86
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.NetFx48", "WebApi.NetFx48\WebApi.NetFx48.csproj", "{F405417B-110F-4A6D-849E-AACDDE33F268}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.Net5", "WebApi.Net5\WebApi.Net5.csproj", "{F25A0C04-86A5-45F5-BE19-0DEF8F42E5AB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F405417B-110F-4A6D-849E-AACDDE33F268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F405417B-110F-4A6D-849E-AACDDE33F268}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F405417B-110F-4A6D-849E-AACDDE33F268}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F405417B-110F-4A6D-849E-AACDDE33F268}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F25A0C04-86A5-45F5-BE19-0DEF8F42E5AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F25A0C04-86A5-45F5-BE19-0DEF8F42E5AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F25A0C04-86A5-45F5-BE19-0DEF8F42E5AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F25A0C04-86A5-45F5-BE19-0DEF8F42E5AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {81604A6A-F5CD-44DF-AA86-039C10809520}
+ EndGlobalSection
+EndGlobal
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.Net5/Controllers/WeatherForecastController.cs b/Coravel/Lab.CoravelScheduler/WebApi.Net5/Controllers/WeatherForecastController.cs
new file mode 100644
index 00000000..7cd628f4
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/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 WebApi.Net5.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/Coravel/Lab.CoravelScheduler/WebApi.Net5/Program.cs b/Coravel/Lab.CoravelScheduler/WebApi.Net5/Program.cs
new file mode 100644
index 00000000..b9a97cb6
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/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 WebApi.Net5
+{
+ 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/Coravel/Lab.CoravelScheduler/WebApi.Net5/Properties/launchSettings.json b/Coravel/Lab.CoravelScheduler/WebApi.Net5/Properties/launchSettings.json
new file mode 100644
index 00000000..286105a3
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/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:21317",
+ "sslPort": 44331
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "WebApi.Net5": {
+ "commandName": "Project",
+ "dotnetRunMessages": "true",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.Net5/Startup.cs b/Coravel/Lab.CoravelScheduler/WebApi.Net5/Startup.cs
new file mode 100644
index 00000000..34d7237e
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/Startup.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Coravel;
+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 WebApi.Net5
+{
+ 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 = "WebApi.Net5", 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", "WebApi.Net5 v1"));
+ }
+
+ app.UseHttpsRedirection();
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
+
+ var provider = app.ApplicationServices;
+ provider.UseScheduler(scheduler =>
+ {
+ scheduler.Schedule(
+ () => Console.WriteLine("Every minute during the week.")
+ )
+ .EveryMinute()
+ .Weekday();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.Net5/WeatherForecast.cs b/Coravel/Lab.CoravelScheduler/WebApi.Net5/WeatherForecast.cs
new file mode 100644
index 00000000..e0b73592
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/WeatherForecast.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace WebApi.Net5
+{
+ 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/Coravel/Lab.CoravelScheduler/WebApi.Net5/WebApi.Net5.csproj b/Coravel/Lab.CoravelScheduler/WebApi.Net5/WebApi.Net5.csproj
new file mode 100644
index 00000000..738a4465
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/WebApi.Net5.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.Development.json b/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.Development.json
new file mode 100644
index 00000000..8983e0fc
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.json b/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.json
new file mode 100644
index 00000000..d9d9a9bf
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.Net5/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/App_Start/DefaultDependencyResolver.cs b/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/App_Start/DefaultDependencyResolver.cs
new file mode 100644
index 00000000..4d95d01d
--- /dev/null
+++ b/Coravel/Lab.CoravelScheduler/WebApi.NetFx48/App_Start/DefaultDependencyResolver.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Web.Http.Dependencies;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace WebApi.NetFx48
+{
+ 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