Browse Source

Replace .Sources package references with local code (#4801)

Replaces package references the following packages with local code:

Microsoft.AspNetCore.Certificates.Generation.Sources
Microsoft.Extensions.CopyOnWriteDictionary.Sources 
Microsoft.Extensions.ClosedGenericMatcher.Sources
Microsoft.Extensions.ObjectMethodExecutor.Sources
Microsoft.Extensions.PropertyActivator.Sources
Microsoft.Extensions.PropertyHelper.Sources
Microsoft.Extensions.RazorViews.Sources
Microsoft.Extensions.SecurityHelper.Sources
Microsoft.Extensions.StackTrace.Sources
Microsoft.Extensions.WebEncoders.Sources
Nate McMaster 7 years ago
parent
commit
13ae0057fb
100 changed files with 7544 additions and 111 deletions
  1. 0 11
      build/dependencies.props
  2. 0 11
      build/external-dependencies.props
  3. 1 0
      build/repo.props
  4. 2 8
      eng/Dependencies.props
  5. 0 2
      src/AuthSamples/build/dependencies.props
  6. 1 0
      src/AzureIntegration/Directory.Build.props
  7. 1 2
      src/AzureIntegration/build/dependencies.props
  8. 1 1
      src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
  9. 1 4
      src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj
  10. 4 1
      src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj
  11. 5 2
      src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj
  12. 1 1
      src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
  13. 4 1
      src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
  14. 4 1
      src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj
  15. 4 1
      src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
  16. 4 1
      src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj
  17. 1 0
      src/Identity/Directory.Build.props
  18. 0 2
      src/Identity/build/dependencies.props
  19. 1 1
      src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
  20. 2 2
      src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
  21. 1 0
      src/Mvc/Directory.Build.props
  22. 0 6
      src/Mvc/build/dependencies.props
  23. 6 3
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj
  24. 5 2
      src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj
  25. 6 6
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj
  26. 5 2
      src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj
  27. 4 1
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj
  28. 5 3
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj
  29. 4 1
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj
  30. 6 3
      src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj
  31. 7 4
      src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj
  32. 1 1
      src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj
  33. 8 5
      src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj
  34. 4 1
      src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj
  35. 0 1
      src/Razor/build/dependencies.props
  36. 0 1
      src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
  37. 1 2
      src/Security/build/dependencies.props
  38. 4 1
      src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj
  39. 4 1
      src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj
  40. 1 1
      src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj
  41. 720 0
      src/Shared/CertificateGeneration/CertificateManager.cs
  42. 12 0
      src/Shared/CertificateGeneration/CertificatePurpose.cs
  43. 20 0
      src/Shared/CertificateGeneration/EnsureCertificateResult.cs
  44. 106 0
      src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs
  45. 155 0
      src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs
  46. 166 0
      src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs
  47. 127 0
      src/Shared/ObjectMethodExecutor/AwaitableInfo.cs
  48. 55 0
      src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs
  49. 340 0
      src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
  50. 114 0
      src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs
  51. 151 0
      src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs
  52. 113 0
      src/Shared/Process/ProcessExtensions.cs
  53. 110 0
      src/Shared/PropertyActivator/PropertyActivator.cs
  54. 526 0
      src/Shared/PropertyHelper/PropertyHelper.cs
  55. 38 0
      src/Shared/RazorViews/AttributeValue.cs
  56. 279 0
      src/Shared/RazorViews/BaseView.cs
  57. 34 0
      src/Shared/RazorViews/HelperResult.cs
  58. 40 0
      src/Shared/SecurityHelper/SecurityHelper.cs
  59. 29 0
      src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs
  60. 170 0
      src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs
  61. 49 0
      src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs
  62. 33 0
      src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs
  63. 135 0
      src/Shared/StackTrace/StackFrame/PortablePdbReader.cs
  64. 18 0
      src/Shared/StackTrace/StackFrame/StackFrameInfo.cs
  65. 54 0
      src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs
  66. 261 0
      src/Shared/StackTrace/StackFrame/StackTraceHelper.cs
  67. 38 0
      src/Shared/WebEncoders/Properties/EncoderResources.cs
  68. 388 0
      src/Shared/WebEncoders/WebEncoders.cs
  69. 360 0
      src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs
  70. 91 0
      src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs
  71. 109 0
      src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs
  72. 29 0
      src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj
  73. 634 0
      src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs
  74. 187 0
      src/Shared/test/Shared.Tests/PropertyActivatorTest.cs
  75. 831 0
      src/Shared/test/Shared.Tests/PropertyHelperTest.cs
  76. 93 0
      src/Shared/test/Shared.Tests/SecurityHelperTests.cs
  77. 345 0
      src/Shared/test/Shared.Tests/StackTraceHelperTest.cs
  78. 113 0
      src/Shared/test/Shared.Tests/WebEncodersTests.cs
  79. 20 0
      src/Shared/test/testassets/ThrowingLibrary/Thrower.cs
  80. 8 0
      src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj
  81. 1 0
      src/SignalR/Directory.Build.props
  82. 1 5
      src/SignalR/build/dependencies.props
  83. 5 2
      src/SignalR/src/Microsoft.AspNetCore.Http.Connections/Microsoft.AspNetCore.Http.Connections.csproj
  84. 5 2
      src/SignalR/src/Microsoft.AspNetCore.SignalR.Core/Microsoft.AspNetCore.SignalR.Core.csproj
  85. 1 0
      src/Templating/Directory.Build.props
  86. 0 2
      src/Templating/build/dependencies.props
  87. 2 2
      src/Templating/test/Templates.Test/Templates.Test.csproj
  88. 1 0
      src/Tools/Directory.Build.props
  89. 1 1
      src/Tools/FirstRunCertGenerator/src/Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj
  90. 6 0
      src/Tools/FirstRunCertGenerator/src/Properties/AssemblyInfo.cs
  91. 300 0
      src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs
  92. 11 0
      src/Tools/FirstRunCertGenerator/test/Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests.csproj
  93. 0 0
      src/Tools/Shared/CommandLine/CliContext.cs
  94. 0 0
      src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs
  95. 0 0
      src/Tools/Shared/CommandLine/ConsoleReporter.cs
  96. 0 0
      src/Tools/Shared/CommandLine/DebugHelper.cs
  97. 0 0
      src/Tools/Shared/CommandLine/Ensure.cs
  98. 0 0
      src/Tools/Shared/CommandLine/IConsole.cs
  99. 0 0
      src/Tools/Shared/CommandLine/IReporter.cs
  100. 0 0
      src/Tools/Shared/CommandLine/NullReporter.cs

+ 0 - 11
build/dependencies.props

@@ -26,14 +26,12 @@
   <PropertyGroup Label="Package Versions: Pinned">
     <!-- Packages that came from aspnet/Extensions. -->
     <MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
-    <MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
     <MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
     <MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.1</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
     <MicrosoftExtensionsCachingAbstractionsPackageVersion>2.1.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
     <MicrosoftExtensionsCachingMemoryPackageVersion>2.1.2</MicrosoftExtensionsCachingMemoryPackageVersion>
     <MicrosoftExtensionsCachingRedisPackageVersion>2.1.2</MicrosoftExtensionsCachingRedisPackageVersion>
     <MicrosoftExtensionsCachingSqlServerPackageVersion>2.1.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
-    <MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
     <MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
     <MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
     <MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
@@ -47,7 +45,6 @@
     <MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
     <MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
     <MicrosoftExtensionsConfigurationXmlPackageVersion>2.1.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
-    <MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
     <MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
     <MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
     <MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>
@@ -73,22 +70,14 @@
     <MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
     <MicrosoftExtensionsLoggingTraceSourcePackageVersion>2.1.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
-    <MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
     <MicrosoftExtensionsObjectPoolPackageVersion>2.1.6</MicrosoftExtensionsObjectPoolPackageVersion>
     <MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
     <MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
     <MicrosoftExtensionsPrimitivesPackageVersion>2.1.6</MicrosoftExtensionsPrimitivesPackageVersion>
-    <MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
-    <MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
-    <MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
-    <MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
-    <MicrosoftExtensionsStackTraceSourcesPackageVersion>2.1.1</MicrosoftExtensionsStackTraceSourcesPackageVersion>
     <MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
     <MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
     <MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
-    <MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
 
     <!-- These dependencies are temporary while we refactor package refs into project refs. -->
     <MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>

+ 0 - 11
build/external-dependencies.props

@@ -15,14 +15,12 @@
   <ItemGroup>
     <!-- Packages from aspnet/Extensions -->
     <ExternalDependency Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)"  />
     <ExternalDependency Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)"  AllMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
-    <ExternalDependency Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="$(MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion)"  AllMetapackage="true" />
@@ -36,7 +34,6 @@
     <ExternalDependency Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
-    <ExternalDependency Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Specification.Tests" Version="$(MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion)"  />
     <ExternalDependency Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
@@ -62,21 +59,13 @@
     <ExternalDependency Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)"  />
     <ExternalDependency Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
-    <ExternalDependency Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)"  AllMetapackage="true" AppMetapackage="true" />
     <ExternalDependency Include="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)"  />
     <ExternalDependency Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
-    <ExternalDependency Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.PropertyActivator.Sources" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.SecurityHelper.Sources" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
-    <ExternalDependency Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
     <ExternalDependency Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
 
     <!-- These dependencies are temporary while we refactor package refs into project refs. -->

+ 1 - 0
build/repo.props

@@ -75,6 +75,7 @@
                       $(RepositoryRoot)src\Html\**\*.*proj;
                       $(RepositoryRoot)src\Servers\**\*.csproj;
                       $(RepositoryRoot)src\Servers\**\*.pkgproj;
+                      $(RepositoryRoot)src\Shared\**\*.*proj;
                       $(RepositoryRoot)src\Tools\**\*.*proj;
                       $(RepositoryRoot)src\Middleware\**\*.*proj;
                       "

+ 2 - 8
eng/Dependencies.props

@@ -25,7 +25,6 @@ and are generated based on the last package release.
   <!-- These dependencies must use version variables because they may be overriden by ProdCon builds. -->
   <ItemGroup Label="ProdCon dependencies">
     <LatestPackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
     <LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
     <LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
@@ -34,14 +33,12 @@ and are generated based on the last package release.
     <LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
@@ -56,12 +53,7 @@ and are generated based on the last package release.
     <LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
-    <LatestPackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
     <LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
     <LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
     <LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
@@ -71,6 +63,7 @@ and are generated based on the last package release.
     <LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
     <LatestPackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
     <LatestPackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
+    <LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
     <LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
     <LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
     <LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
@@ -79,6 +72,7 @@ and are generated based on the last package release.
 
   <ItemGroup Label="External dependencies">
     <LatestPackageReference Include="BenchmarkDotNet" Version="0.10.13" />
+    <LatestPackageReference Include="FSharp.Core" Version="4.2.1" />
     <LatestPackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
     <LatestPackageReference Include="Moq" Version="4.10.0" />
     <!-- This version is required by MSBuild tasks or Visual Studio extensions. -->

+ 0 - 2
src/AuthSamples/build/dependencies.props

@@ -57,8 +57,6 @@
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
     <MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
-    <MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
     <MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.14.2</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
     <MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.2.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>
     <MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>

+ 1 - 0
src/AzureIntegration/Directory.Build.props

@@ -16,6 +16,7 @@
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
   </PropertyGroup>
 
 </Project>

+ 1 - 2
src/AzureIntegration/build/dependencies.props

@@ -51,6 +51,5 @@
     <MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
     <MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
-    <MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
   </PropertyGroup>
-</Project>
+</Project>

+ 1 - 1
src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj

@@ -9,13 +9,13 @@
     <None Include="Assets\*.*" CopyToOutputDirectory="PreserveNewest" />
     <Compile Remove="Assets\**\*.*" />
     <Compile Include="Assets\RuntimeInformationMiddleware.cs" />
+    <Compile Include="$(SharedSourceRoot)Process\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
 
     <PackageReference Include="Microsoft.Azure.Management.Fluent" Version="$(MicrosoftAzureManagementFluentPackageVersion)" />
     <PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStoragePackageVersion)" />

+ 1 - 4
src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj

@@ -12,10 +12,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector</Description>
 
   <ItemGroup>
     <Compile Include="..\..\shared\src\*.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Reference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" />
+    <Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
   </ItemGroup>
 
 </Project>

+ 4 - 1
src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj

@@ -8,10 +8,13 @@
     <PackageTags>aspnetcore;json;jsonpatch</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="Newtonsoft.Json" />
-    <Reference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" />
   </ItemGroup>
 
 </Project>

+ 5 - 2
src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj

@@ -8,6 +8,11 @@
     <PackageTags>aspnetcore;hosting</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
+    <Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.Http.Extensions" />
@@ -20,8 +25,6 @@
     <Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
     <Reference Include="Microsoft.Extensions.Logging" />
     <Reference Include="Microsoft.Extensions.Options" />
-    <Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
-    <Reference Include="Microsoft.Extensions.StackTrace.Sources" PrivateAssets="All" />
     <Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
     <Reference Include="System.Diagnostics.DiagnosticSource" />
     <Reference Include="System.Reflection.Metadata" />

+ 1 - 1
src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj

@@ -16,6 +16,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)Process\*.cs" />
     <EmbeddedResource Include="Deployers\RemoteWindowsDeployer\RemotePSSessionHelper.ps1;Deployers\RemoteWindowsDeployer\StartServer.ps1;Deployers\RemoteWindowsDeployer\StopServer.ps1" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
   </ItemGroup>
 
@@ -25,7 +26,6 @@
     <Reference Include="Microsoft.Extensions.Logging" />
     <Reference Include="Microsoft.Extensions.Logging.Console" />
     <Reference Include="Microsoft.Extensions.Logging.Testing" />
-    <Reference Include="Microsoft.Extensions.Process.Sources" PrivateAssets="All" />
     <Reference Include="Microsoft.NETCore.Windows.ApiSets" />
     <Reference Include="Serilog.Extensions.Logging" />
     <Reference Include="Serilog.Sinks.File" />

+ 4 - 1
src/Http/Http/src/Microsoft.AspNetCore.Http.csproj

@@ -9,10 +9,13 @@
     <PackageTags>aspnetcore</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.WebUtilities" />
-    <Reference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" />
     <Reference Include="Microsoft.Extensions.ObjectPool" />
     <Reference Include="Microsoft.Extensions.Options" />
     <Reference Include="Microsoft.Net.Http.Headers" />

+ 4 - 1
src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj

@@ -11,8 +11,11 @@ Microsoft.AspNetCore.Routing.RouteData</Description>
     <PackageTags>aspnetcore;routing</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
-    <Reference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" />
   </ItemGroup>
 </Project>

+ 4 - 1
src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj

@@ -11,6 +11,10 @@ Microsoft.AspNetCore.Routing.RouteCollection</Description>
     <PackageTags>aspnetcore;routing</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Http.Extensions" />
     <Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
@@ -18,6 +22,5 @@ Microsoft.AspNetCore.Routing.RouteCollection</Description>
     <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
     <Reference Include="Microsoft.Extensions.ObjectPool" />
     <Reference Include="Microsoft.Extensions.Options" />
-    <Reference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" />
   </ItemGroup>
 </Project>

+ 4 - 1
src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj

@@ -10,7 +10,10 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <Reference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" />
+    <Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
     <Reference Include="Microsoft.Net.Http.Headers" />
     <Reference Include="System.Text.Encodings.Web" />
   </ItemGroup>

+ 1 - 0
src/Identity/Directory.Build.props

@@ -15,6 +15,7 @@
     <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
   </PropertyGroup>
 
 </Project>

+ 0 - 2
src/Identity/build/dependencies.props

@@ -73,8 +73,6 @@
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
     <MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
-    <MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
     <MicrosoftNETSdkRazorPackageVersion>2.1.1</MicrosoftNETSdkRazorPackageVersion>
     <MicrosoftOwinSecurityInteropPackageVersion>2.1.1</MicrosoftOwinSecurityInteropPackageVersion>
   </PropertyGroup>

+ 1 - 1
src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj

@@ -10,12 +10,12 @@
 
   <ItemGroup>
     <Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
+    <Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
     <Reference Include="Microsoft.EntityFrameworkCore.Relational" />
-    <Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj

@@ -10,6 +10,8 @@
 
   <ItemGroup>
     <Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
+    <Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
+    <Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
@@ -20,8 +22,6 @@
     <Reference Include="Microsoft.Extensions.FileProviders.Physical" />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
     <Reference Include="Microsoft.Extensions.Options" />
-    <Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
-    <Reference Include="Microsoft.Extensions.StackTrace.Sources" PrivateAssets="All" />
     <Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
     <Reference Include="System.Diagnostics.DiagnosticSource" />
     <Reference Include="System.Reflection.Metadata" />

+ 1 - 0
src/Mvc/Directory.Build.props

@@ -16,6 +16,7 @@
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
   </PropertyGroup>
 
 </Project>

+ 0 - 6
src/Mvc/build/dependencies.props

@@ -71,10 +71,8 @@
     <MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.1</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
     <MicrosoftCodeAnalysisRazorPackageVersion>2.1.1</MicrosoftCodeAnalysisRazorPackageVersion>
     <MicrosoftExtensionsCachingMemoryPackageVersion>2.1.1</MicrosoftExtensionsCachingMemoryPackageVersion>
-    <MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
     <MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
     <MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
-    <MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
     <MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
     <MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.1.0</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
     <MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
@@ -88,13 +86,9 @@
     <MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
     <MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
-    <MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
     <MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
     <MicrosoftExtensionsPrimitivesPackageVersion>2.1.1</MicrosoftExtensionsPrimitivesPackageVersion>
-    <MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
-    <MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
     <MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
     <MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
     <MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>

+ 6 - 3
src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj

@@ -10,12 +10,15 @@ Microsoft.AspNetCore.Mvc.IActionResult</Description>
     <PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Routing.Abstractions" Version="$(MicrosoftAspNetCoreRoutingAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
   </ItemGroup>
 

+ 5 - 2
src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj

@@ -8,12 +8,15 @@
     <PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
 
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 
 </Project>

+ 6 - 6
src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj

@@ -17,7 +17,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
   </PropertyGroup>
 
   <ItemGroup>
-      <Compile Include="$(RepositoryRoot)\src\Shared\RangeHelper\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+    <Compile Include="$(SharedSourceRoot)RangeHelper\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
@@ -30,17 +35,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
     <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" Version="$(MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
     <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourcePackageVersion)" />

+ 5 - 2
src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj

@@ -8,11 +8,14 @@
     <PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
 
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
     <PackageReference Include="System.ComponentModel.Annotations" Version="$(SystemComponentModelAnnotationsPackageVersion)" />
   </ItemGroup>

+ 4 - 1
src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj

@@ -8,10 +8,13 @@
     <PackageTags>aspnetcore;aspnetcoremvc;json</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
 
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="$(MicrosoftAspNetCoreJsonPatchPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
   </ItemGroup>
 </Project>

+ 5 - 3
src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj

@@ -9,9 +9,11 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
 
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
+  <ItemGroup>
+    <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
   </ItemGroup>
 </Project>

+ 4 - 1
src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj

@@ -11,13 +11,16 @@ Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer</Description>
     <PackageTags>aspnetcore;aspnetcoremvc;localization</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
 
     <PackageReference Include="Microsoft.AspNetCore.Localization" Version="$(MicrosoftAspNetCoreLocalizationPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 
 </Project>

+ 6 - 3
src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj

@@ -10,6 +10,12 @@
     <Serviceable>true</Serviceable>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.ViewFeatures\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
 
@@ -19,10 +25,7 @@
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 
   <Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec" DependsOnTargets="BuiltProjectOutputGroup;DebugSymbolsProjectOutputGroup;DocumentationProjectOutputGroup">

+ 7 - 4
src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj

@@ -8,14 +8,17 @@
     <PackageTags>aspnetcore;aspnetcoremvc;cshtml;razor</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
 
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj

@@ -10,6 +10,7 @@
 
   <ItemGroup>
     <EmbeddedResource Include="compiler\resources\**\*" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
@@ -21,6 +22,5 @@
     <PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 </Project>

+ 8 - 5
src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj

@@ -13,6 +13,14 @@ Microsoft.AspNetCore.Mvc.ViewComponent</Description>
     <PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.DataAnnotations\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
@@ -22,12 +30,7 @@ Microsoft.AspNetCore.Mvc.ViewComponent</Description>
     <PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="$(MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
     <PackageReference Include="Newtonsoft.Json.Bson" Version="$(NewtonsoftJsonBsonPackageVersion)" />

+ 4 - 1
src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj

@@ -10,13 +10,16 @@ System.Web.Http.ApiController</Description>
     <PackageTags>aspnetcore;aspnetcoremvc;aspnetwebapi</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
     <ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Formatters.Json\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
 
     <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
   </ItemGroup>
 
 </Project>

+ 0 - 1
src/Razor/build/dependencies.props

@@ -65,7 +65,6 @@
     <MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
     <MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
     <MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
-    <MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
     <MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
     <MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
   </PropertyGroup>

+ 0 - 1
src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj

@@ -12,7 +12,6 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
   </ItemGroup>
 
 </Project>

+ 1 - 2
src/Security/build/dependencies.props

@@ -52,7 +52,6 @@
     <MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
     <MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
     <MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
   </PropertyGroup>
-</Project>
+</Project>

+ 4 - 1
src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj

@@ -8,6 +8,10 @@
     <PackageTags>aspnetcore;authentication;security</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(MicrosoftAspNetCoreAuthenticationCorePackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="$(MicrosoftAspNetCoreDataProtectionPackageVersion)" />
@@ -15,7 +19,6 @@
     <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
   </ItemGroup>
 

+ 4 - 1
src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj

@@ -8,13 +8,16 @@
     <PackageTags>aspnetcore;authorization</PackageTags>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Authorization\Microsoft.AspNetCore.Authorization.csproj" />
   </ItemGroup>
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="$(MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj

@@ -11,10 +11,10 @@
 
   <ItemGroup>
     <Compile Include="$(KestrelSharedSourceRoot)src\ThrowHelper.cs" />
+    <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
-    <Reference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" />
     <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.WebUtilities" />

+ 720 - 0
src/Shared/CertificateGeneration/CertificateManager.cs

@@ -0,0 +1,720 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+    internal class CertificateManager
+    {
+        public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
+        public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate";
+
+        public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2";
+        public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate";
+
+        private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1";
+        private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication";
+
+        private const string LocalhostHttpsDnsName = "localhost";
+        private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName;
+
+        private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing";
+
+        public const int RSAMinimumKeySizeInBits = 2048;
+
+        private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1);
+        private const string CertificateSubjectRegex = "CN=(.*[^,]+).*";
+        private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain";
+        private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db";
+        private const string MacOSFindCertificateCommandLine = "security";
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+        private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain;
+#endif
+        private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)";
+        private const string MacOSRemoveCertificateTrustCommandLine = "sudo";
+        private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}";
+        private const string MacOSDeleteCertificateCommandLine = "sudo";
+        private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}";
+        private const string MacOSTrustCertificateCommandLine = "sudo";
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+        private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " ";
+#endif
+        private const int UserCancelledErrorCode = 1223;
+
+        public IList<X509Certificate2> ListCertificates(
+            CertificatePurpose purpose,
+            StoreName storeName,
+            StoreLocation location,
+            bool isValid,
+            bool requireExportable = true)
+        {
+            var certificates = new List<X509Certificate2>();
+            try
+            {
+                using (var store = new X509Store(storeName, location))
+                {
+                    store.Open(OpenFlags.ReadOnly);
+                    certificates.AddRange(store.Certificates.OfType<X509Certificate2>());
+                    IEnumerable<X509Certificate2> matchingCertificates = certificates;
+                    switch (purpose)
+                    {
+                        case CertificatePurpose.All:
+                            matchingCertificates = matchingCertificates
+                                .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid));
+                            break;
+                        case CertificatePurpose.HTTPS:
+                            matchingCertificates = matchingCertificates
+                                .Where(c => HasOid(c, AspNetHttpsOid));
+                            break;
+                        case CertificatePurpose.Signing:
+                            matchingCertificates = matchingCertificates
+                                .Where(c => HasOid(c, AspNetIdentityOid));
+                            break;
+                        default:
+                            break;
+                    }
+                    if (isValid)
+                    {
+                        // Ensure the certificate hasn't expired, has a private key and its exportable
+                        // (for container/unix scenarios).
+                        var now = DateTimeOffset.Now;
+                        matchingCertificates = matchingCertificates
+                            .Where(c => c.NotBefore <= now &&
+                                now <= c.NotAfter &&
+                                (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c)));
+                    }
+
+                    // We need to enumerate the certificates early to prevent dispoisng issues.
+                    matchingCertificates = matchingCertificates.ToList();
+
+                    var certificatesToDispose = certificates.Except(matchingCertificates);
+                    DisposeCertificates(certificatesToDispose);
+
+                    store.Close();
+
+                    return (IList<X509Certificate2>)matchingCertificates;
+                }
+            }
+            catch
+            {
+                DisposeCertificates(certificates);
+                certificates.Clear();
+                return certificates;
+            }
+
+            bool HasOid(X509Certificate2 certificate, string oid) =>
+                certificate.Extensions.OfType<X509Extension>()
+                    .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal));
+#if !XPLAT
+            bool IsExportable(X509Certificate2 c) =>
+                ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
+                    rsaPrivateKey.CspKeyContainerInfo.Exportable) ||
+                (c.GetRSAPrivateKey() is RSACng cngPrivateKey &&
+                    cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport));
+#else
+            // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as
+            // System.Security.Cryptography.Cng is not pat of the shared framework and we don't
+            // want to bring the dependency in on CLI scenarios. This functionality will be used
+            // on CLI scenarios as part of the first run experience, so checking the exportability
+            // of the certificate is not important.
+            bool IsExportable(X509Certificate2 c) =>
+                ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
+                    rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider));
+#endif
+        }
+
+        private void DisposeCertificates(IEnumerable<X509Certificate2> disposables)
+        {
+            foreach (var disposable in disposables)
+            {
+                try
+                {
+                    disposable.Dispose();
+                }
+                catch
+                {
+                }
+            }
+        }
+
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+
+        public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
+        {
+            var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName);
+            var extensions = new List<X509Extension>();
+            var sanBuilder = new SubjectAlternativeNameBuilder();
+            sanBuilder.AddDnsName(LocalhostHttpsDnsName);
+
+            var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true);
+            var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
+                new OidCollection() {
+                    new Oid(
+                        ServerAuthenticationEnhancedKeyUsageOid,
+                        ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
+                },
+                critical: true);
+
+            var basicConstraints = new X509BasicConstraintsExtension(
+                certificateAuthority: false,
+                hasPathLengthConstraint: false,
+                pathLengthConstraint: 0,
+                critical: true);
+
+            var aspNetHttpsExtension = new X509Extension(
+                new AsnEncodedData(
+                    new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName),
+                    Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)),
+                critical: false);
+
+            extensions.Add(basicConstraints);
+            extensions.Add(keyUsage);
+            extensions.Add(enhancedKeyUsage);
+            extensions.Add(sanBuilder.Build(critical: true));
+            extensions.Add(aspNetHttpsExtension);
+
+            var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                certificate.FriendlyName = AspNetHttpsOidFriendlyName;
+            }
+
+            return certificate;
+        }
+
+        public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
+        {
+            var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName);
+            var extensions = new List<X509Extension>();
+
+            var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true);
+            var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
+                new OidCollection() {
+                    new Oid(
+                        ServerAuthenticationEnhancedKeyUsageOid,
+                        ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
+                },
+                critical: true);
+
+            var basicConstraints = new X509BasicConstraintsExtension(
+                certificateAuthority: false,
+                hasPathLengthConstraint: false,
+                pathLengthConstraint: 0,
+                critical: true);
+
+            var aspNetIdentityExtension = new X509Extension(
+                new AsnEncodedData(
+                    new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName),
+                    Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)),
+                critical: false);
+
+            extensions.Add(basicConstraints);
+            extensions.Add(keyUsage);
+            extensions.Add(enhancedKeyUsage);
+            extensions.Add(aspNetIdentityExtension);
+
+            var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                certificate.FriendlyName = AspNetIdentityOidFriendlyName;
+            }
+
+            return certificate;
+        }
+
+        public X509Certificate2 CreateSelfSignedCertificate(
+            X500DistinguishedName subject,
+            IEnumerable<X509Extension> extensions,
+            DateTimeOffset notBefore,
+            DateTimeOffset notAfter)
+        {
+            var key = CreateKeyMaterial(RSAMinimumKeySizeInBits);
+
+            var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+            foreach (var extension in extensions)
+            {
+                request.CertificateExtensions.Add(extension);
+            }
+
+            return request.CreateSelfSigned(notBefore, notAfter);
+
+            RSA CreateKeyMaterial(int minimumKeySize)
+            {
+                var rsa = RSA.Create(minimumKeySize);
+                if (rsa.KeySize < minimumKeySize)
+                {
+                    throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits");
+                }
+
+                return rsa;
+            }
+        }
+
+        public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location)
+        {
+            var imported = certificate;
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+            {
+                // On non OSX systems we need to export the certificate and import it so that the transient
+                // key that we generated gets persisted.
+                var export = certificate.Export(X509ContentType.Pkcs12, "");
+                imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
+                Array.Clear(export, 0, export.Length);
+            }
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                imported.FriendlyName = certificate.FriendlyName;
+            }
+
+            using (var store = new X509Store(name, location))
+            {
+                store.Open(OpenFlags.ReadWrite);
+                store.Add(imported);
+                store.Close();
+            };
+
+            return imported;
+        }
+
+        public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password)
+        {
+            if (Path.GetDirectoryName(path) != "")
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(path));
+            }
+
+            if (includePrivateKey)
+            {
+                var bytes = certificate.Export(X509ContentType.Pkcs12, password);
+                try
+                {
+                    File.WriteAllBytes(path, bytes);
+                }
+                finally
+                {
+                    Array.Clear(bytes, 0, bytes.Length);
+                }
+            }
+            else
+            {
+                var bytes = certificate.Export(X509ContentType.Cert);
+                File.WriteAllBytes(path, bytes);
+            }
+        }
+
+        public void TrustCertificate(X509Certificate2 certificate)
+        {
+            // Strip certificate of the private key if any.
+            var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert));
+
+            if (!IsTrusted(publicCertificate))
+            {
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                {
+                    TrustCertificateOnWindows(certificate, publicCertificate);
+                }
+                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+                {
+                    TrustCertificateOnMac(publicCertificate);
+                }
+            }
+        }
+
+        private void TrustCertificateOnMac(X509Certificate2 publicCertificate)
+        {
+            var tmpFile = Path.GetTempFileName();
+            try
+            {
+                ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null);
+                using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile))
+                {
+                    process.WaitForExit();
+                    if (process.ExitCode != 0)
+                    {
+                        throw new InvalidOperationException("There was an error trusting the certificate.");
+                    }
+                }
+            }
+            finally
+            {
+                try
+                {
+                    if (File.Exists(tmpFile))
+                    {
+                        File.Delete(tmpFile);
+                    }
+                }
+                catch
+                {
+                    // We don't care if we can't delete the temp file.
+                }
+            }
+        }
+
+        private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate)
+        {
+            publicCertificate.FriendlyName = certificate.FriendlyName;
+
+            using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
+            {
+                store.Open(OpenFlags.ReadWrite);
+                try
+                {
+                    store.Add(publicCertificate);
+                }
+                catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode)
+                {
+                    throw new UserCancelledTrustException();
+                }
+                store.Close();
+            };
+        }
+
+        public bool IsTrusted(X509Certificate2 certificate)
+        {
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false)
+                    .Any(c => c.Thumbprint == certificate.Thumbprint);
+            }
+            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+            {
+                var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout);
+                if (!subjectMatch.Success)
+                {
+                    throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'.");
+                }
+                var subject = subjectMatch.Groups[1].Value;
+                using (var checkTrustProcess = Process.Start(new ProcessStartInfo(
+                    MacOSFindCertificateCommandLine,
+                    string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject))
+                {
+                    RedirectStandardOutput = true
+                }))
+                {
+                    var output = checkTrustProcess.StandardOutput.ReadToEnd();
+                    checkTrustProcess.WaitForExit();
+                    var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout);
+                    var hashes = matches.OfType<Match>().Select(m => m.Groups[1].Value).ToList();
+                    return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal));
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName)
+        {
+            CleanupCertificates(CertificatePurpose.HTTPS, subject);
+        }
+
+        public void CleanupCertificates(CertificatePurpose purpose, string subject)
+        {
+            // On OS X we don't have a good way to manage trusted certificates in the system keychain
+            // so we do everything by invoking the native toolchain.
+            // This has some limitations, like for example not being able to identify our custom OID extension. For that
+            // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates.
+            // To do this, we list the certificates that we can identify on the current user personal store and we invoke
+            // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates,
+            // we remove the certificates from the local user store to finish up the cleanup.
+            var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false);
+            foreach (var certificate in certificates)
+            {
+                RemoveCertificate(certificate, RemoveLocations.All);
+            }
+        }
+
+        public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null)
+        {
+            var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
+                ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) :
+                ListCertificates(purpose, storeName, storeLocation, isValid: false);
+            var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject);
+
+            var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted;
+
+            foreach (var certificate in certificates)
+            {
+                RemoveCertificate(certificate, removeLocation);
+            }
+
+            DisposeCertificates(certificates);
+        }
+
+        private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations)
+        {
+            switch (locations)
+            {
+                case RemoveLocations.Undefined:
+                    throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location.");
+                case RemoveLocations.Local:
+                    RemoveCertificateFromUserStore(certificate);
+                    break;
+                case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux):
+                    RemoveCertificateFromTrustedRoots(certificate);
+                    break;
+                case RemoveLocations.All:
+                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                    {
+                        RemoveCertificateFromTrustedRoots(certificate);
+                    }
+                    RemoveCertificateFromUserStore(certificate);
+                    break;
+                default:
+                    throw new InvalidOperationException("Invalid location.");
+            }
+        }
+
+        private static void RemoveCertificateFromUserStore(X509Certificate2 certificate)
+        {
+            using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+            {
+                store.Open(OpenFlags.ReadWrite);
+                var matching = store.Certificates
+                    .OfType<X509Certificate2>()
+                    .Single(c => c.SerialNumber == certificate.SerialNumber);
+
+                store.Remove(matching);
+                store.Close();
+            }
+        }
+
+        private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate)
+        {
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
+                {
+                    store.Open(OpenFlags.ReadWrite);
+                    var matching = store.Certificates
+                        .OfType<X509Certificate2>()
+                        .Single(c => c.SerialNumber == certificate.SerialNumber);
+
+                    store.Remove(matching);
+                    store.Close();
+                }
+            }
+            else
+            {
+                if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain
+                {
+                    try
+                    {
+                        RemoveCertificateTrustRule(certificate);
+                    }
+                    catch
+                    {
+                        // We don't care if we fail to remove the trust rule if
+                        // for some reason the certificate became untrusted.
+                        // The delete command will fail if the certificate is
+                        // trusted.
+                    }
+                    RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate);
+                }
+            }
+        }
+
+        private static void RemoveCertificateTrustRule(X509Certificate2 certificate)
+        {
+            var certificatePath = Path.GetTempFileName();
+            try
+            {
+                var certBytes = certificate.Export(X509ContentType.Cert);
+                File.WriteAllBytes(certificatePath, certBytes);
+                var processInfo = new ProcessStartInfo(
+                    MacOSRemoveCertificateTrustCommandLine,
+                    string.Format(
+                        MacOSRemoveCertificateTrustCommandLineArgumentsFormat,
+                        certificatePath
+                    ));
+                using (var process = Process.Start(processInfo))
+                {
+                    process.WaitForExit();
+                }
+            }
+            finally
+            {
+                try
+                {
+                    if (File.Exists(certificatePath))
+                    {
+                        File.Delete(certificatePath);
+                    }
+                }
+                catch
+                {
+                    // We don't care about failing to do clean-up on a temp file.
+                }
+            }
+        }
+
+        private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate)
+        {
+            var processInfo = new ProcessStartInfo(
+                MacOSDeleteCertificateCommandLine,
+                string.Format(
+                    MacOSDeleteCertificateCommandLineArgumentsFormat,
+                    certificate.Thumbprint.ToUpperInvariant(),
+                    keyChain
+                ))
+            {
+                RedirectStandardOutput = true,
+                RedirectStandardError = true
+            };
+
+            using (var process = Process.Start(processInfo))
+            {
+                var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd();
+                process.WaitForExit();
+
+                if (process.ExitCode != 0)
+                {
+                    throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'.
+
+{output}");
+                }
+            }
+        }
+
+        public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
+            DateTimeOffset notBefore,
+            DateTimeOffset notAfter,
+            string path = null,
+            bool trust = false,
+            bool includePrivateKey = false,
+            string password = null,
+            string subject = LocalhostHttpsDistinguishedName)
+        {
+            return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
+        }
+
+        public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate(
+            DateTimeOffset notBefore,
+            DateTimeOffset notAfter,
+            string path = null,
+            bool trust = false,
+            bool includePrivateKey = false,
+            string password = null,
+            string subject = IdentityDistinguishedName)
+        {
+            return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject);
+        }
+
+        public EnsureCertificateResult EnsureValidCertificateExists(
+            DateTimeOffset notBefore,
+            DateTimeOffset notAfter,
+            CertificatePurpose purpose,
+            string path = null,
+            bool trust = false,
+            bool includePrivateKey = false,
+            string password = null,
+            string subjectOverride = null)
+        {
+            if (purpose == CertificatePurpose.All)
+            {
+                throw new ArgumentException("The certificate must have a specific purpose.");
+            }
+
+            var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat(
+                ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true));
+
+            certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride);
+
+            var result = EnsureCertificateResult.Succeeded;
+
+            X509Certificate2 certificate = null;
+            if (certificates.Count() > 0)
+            {
+                certificate = certificates.FirstOrDefault();
+                result = EnsureCertificateResult.ValidCertificatePresent;
+            }
+            else
+            {
+                try
+                {
+                    switch (purpose)
+                    {
+                        case CertificatePurpose.All:
+                            throw new InvalidOperationException("The certificate must have a specific purpose.");
+                        case CertificatePurpose.HTTPS:
+                            certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride);
+                            break;
+                        case CertificatePurpose.Signing:
+                            certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride);
+                            break;
+                        default:
+                            throw new InvalidOperationException("The certificate must have a purpose.");
+                    }
+                }
+                catch
+                {
+                    return EnsureCertificateResult.ErrorCreatingTheCertificate;
+                }
+
+                try
+                {
+                    certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser);
+                }
+                catch
+                {
+                    return EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore;
+                }
+            }
+            if (path != null)
+            {
+                try
+                {
+                    ExportCertificate(certificate, path, includePrivateKey, password);
+                }
+                catch
+                {
+                    return EnsureCertificateResult.ErrorExportingTheCertificate;
+                }
+            }
+
+            if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust)
+            {
+                try
+                {
+                    TrustCertificate(certificate);
+                }
+                catch (UserCancelledTrustException)
+                {
+                    return EnsureCertificateResult.UserCancelledTrustStep;
+                }
+                catch
+                {
+                    return EnsureCertificateResult.FailedToTrustTheCertificate;
+                }
+            }
+
+            return result;
+        }
+
+        private class UserCancelledTrustException : Exception
+        {
+        }
+
+        private enum RemoveLocations
+        {
+            Undefined,
+            Local,
+            Trusted,
+            All
+        }
+#endif
+    }
+}

+ 12 - 0
src/Shared/CertificateGeneration/CertificatePurpose.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+    internal enum CertificatePurpose
+    {
+        All,
+        HTTPS,
+        Signing
+    }
+}

+ 20 - 0
src/Shared/CertificateGeneration/EnsureCertificateResult.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+    internal enum EnsureCertificateResult
+    {
+        Succeeded = 1,
+        ValidCertificatePresent,
+        ErrorCreatingTheCertificate,
+        ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore,
+        ErrorExportingTheCertificate,
+        FailedToTrustTheCertificate,
+        UserCancelledTrustStep
+    }
+}
+
+#endif

+ 106 - 0
src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs

@@ -0,0 +1,106 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+    /// <summary>
+    /// Helper related to generic interface definitions and implementing classes.
+    /// </summary>
+    internal static class ClosedGenericMatcher
+    {
+        /// <summary>
+        /// Determine whether <paramref name="queryType"/> is or implements a closed generic <see cref="Type"/>
+        /// created from <paramref name="interfaceType"/>.
+        /// </summary>
+        /// <param name="queryType">The <see cref="Type"/> of interest.</param>
+        /// <param name="interfaceType">The open generic <see cref="Type"/> to match. Usually an interface.</param>
+        /// <returns>
+        /// The closed generic <see cref="Type"/> created from <paramref name="interfaceType"/> that
+        /// <paramref name="queryType"/> is or implements. <c>null</c> if the two <see cref="Type"/>s have no such
+        /// relationship.
+        /// </returns>
+        /// <remarks>
+        /// This method will return <paramref name="queryType"/> if <paramref name="interfaceType"/> is
+        /// <c>typeof(KeyValuePair{,})</c>, and <paramref name="queryType"/> is
+        /// <c>typeof(KeyValuePair{string, object})</c>.
+        /// </remarks>
+        public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
+        {
+            if (queryType == null)
+            {
+                throw new ArgumentNullException(nameof(queryType));
+            }
+
+            if (interfaceType == null)
+            {
+                throw new ArgumentNullException(nameof(interfaceType));
+            }
+
+            if (IsGenericInstantiation(queryType, interfaceType))
+            {
+                // queryType matches (i.e. is a closed generic type created from) the open generic type.
+                return queryType;
+            }
+
+            // Otherwise check all interfaces the type implements for a match.
+            // - If multiple different generic instantiations exists, we want the most derived one.
+            // - If that doesn't break the tie, then we sort alphabetically so that it's deterministic.
+            //
+            // We do this by looking at interfaces on the type, and recursing to the base type 
+            // if we don't find any matches.
+            return GetGenericInstantiation(queryType, interfaceType);
+        }
+
+        private static bool IsGenericInstantiation(Type candidate, Type interfaceType)
+        {
+            return
+                candidate.GetTypeInfo().IsGenericType &&
+                candidate.GetGenericTypeDefinition() == interfaceType;
+        }
+
+        private static Type GetGenericInstantiation(Type queryType, Type interfaceType)
+        {
+            Type bestMatch = null;
+            var interfaces = queryType.GetInterfaces();
+            foreach (var @interface in interfaces)
+            {
+                if (IsGenericInstantiation(@interface, interfaceType))
+                {
+                    if (bestMatch == null)
+                    {
+                        bestMatch = @interface;
+                    }
+                    else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0)
+                    {
+                        bestMatch = @interface;
+                    }
+                    else
+                    {
+                        // There are two matches at this level of the class hierarchy, but @interface is after
+                        // bestMatch in the sort order.
+                    }
+                }
+            }
+
+            if (bestMatch != null)
+            {
+                return bestMatch;
+            }
+
+            // BaseType will be null for object and interfaces, which means we've reached 'bottom'.
+            var baseType = queryType?.GetTypeInfo().BaseType;
+            if (baseType == null)
+            {
+                return null;
+            }
+            else
+            {
+                return GetGenericInstantiation(baseType, interfaceType);
+            }
+        }
+    }
+}

+ 155 - 0
src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs

@@ -0,0 +1,155 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal class CopyOnWriteDictionary<TKey, TValue> : IDictionary<TKey, TValue>
+    {
+        private readonly IDictionary<TKey, TValue> _sourceDictionary;
+        private readonly IEqualityComparer<TKey> _comparer;
+        private IDictionary<TKey, TValue> _innerDictionary;
+
+        public CopyOnWriteDictionary(
+            IDictionary<TKey, TValue> sourceDictionary,
+            IEqualityComparer<TKey> comparer)
+        {
+            if (sourceDictionary == null)
+            {
+                throw new ArgumentNullException(nameof(sourceDictionary));
+            }
+
+            if (comparer == null)
+            {
+                throw new ArgumentNullException(nameof(comparer));
+            }
+
+            _sourceDictionary = sourceDictionary;
+            _comparer = comparer;
+        }
+
+        private IDictionary<TKey, TValue> ReadDictionary
+        {
+            get
+            {
+                return _innerDictionary ?? _sourceDictionary;
+            }
+        }
+
+        private IDictionary<TKey, TValue> WriteDictionary
+        {
+            get
+            {
+                if (_innerDictionary == null)
+                {
+                    _innerDictionary = new Dictionary<TKey, TValue>(_sourceDictionary,
+                                                                    _comparer);
+                }
+
+                return _innerDictionary;
+            }
+        }
+
+        public virtual ICollection<TKey> Keys
+        {
+            get
+            {
+                return ReadDictionary.Keys;
+            }
+        }
+
+        public virtual ICollection<TValue> Values
+        {
+            get
+            {
+                return ReadDictionary.Values;
+            }
+        }
+
+        public virtual int Count
+        {
+            get
+            {
+                return ReadDictionary.Count;
+            }
+        }
+
+        public virtual bool IsReadOnly
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        public virtual TValue this[TKey key]
+        {
+            get
+            {
+                return ReadDictionary[key];
+            }
+            set
+            {
+                WriteDictionary[key] = value;
+            }
+        }
+
+        public virtual bool ContainsKey(TKey key)
+        {
+            return ReadDictionary.ContainsKey(key);
+        }
+
+        public virtual void Add(TKey key, TValue value)
+        {
+            WriteDictionary.Add(key, value);
+        }
+
+        public virtual bool Remove(TKey key)
+        {
+            return WriteDictionary.Remove(key);
+        }
+
+        public virtual bool TryGetValue(TKey key, out TValue value)
+        {
+            return ReadDictionary.TryGetValue(key, out value);
+        }
+
+        public virtual void Add(KeyValuePair<TKey, TValue> item)
+        {
+            WriteDictionary.Add(item);
+        }
+
+        public virtual void Clear()
+        {
+            WriteDictionary.Clear();
+        }
+
+        public virtual bool Contains(KeyValuePair<TKey, TValue> item)
+        {
+            return ReadDictionary.Contains(item);
+        }
+
+        public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+        {
+            ReadDictionary.CopyTo(array, arrayIndex);
+        }
+
+        public bool Remove(KeyValuePair<TKey, TValue> item)
+        {
+            return WriteDictionary.Remove(item);
+        }
+
+        public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            return ReadDictionary.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+    }
+}

+ 166 - 0
src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs

@@ -0,0 +1,166 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal struct CopyOnWriteDictionaryHolder<TKey, TValue>
+    {
+        private readonly Dictionary<TKey, TValue> _source;
+        private Dictionary<TKey, TValue> _copy;
+
+        public CopyOnWriteDictionaryHolder(Dictionary<TKey, TValue> source)
+        {
+            if (source == null)
+            {
+                throw new ArgumentNullException(nameof(source));
+            }
+
+            _source = source;
+            _copy = null;
+        }
+
+        public CopyOnWriteDictionaryHolder(CopyOnWriteDictionaryHolder<TKey, TValue> source)
+        {
+            _source = source._copy ?? source._source;
+            _copy = null;
+        }
+
+        public bool HasBeenCopied => _copy != null;
+
+        public Dictionary<TKey, TValue> ReadDictionary
+        {
+            get
+            {
+                if (_copy != null)
+                {
+                    return _copy;
+                }
+                else if (_source != null)
+                {
+                    return _source;
+                }
+                else
+                {
+                    // Default-Constructor case
+                    _copy = new Dictionary<TKey, TValue>();
+                    return _copy;
+                }
+            }
+        }
+
+        public Dictionary<TKey, TValue> WriteDictionary
+        {
+            get
+            {
+                if (_copy == null && _source == null)
+                {
+                    // Default-Constructor case
+                    _copy = new Dictionary<TKey, TValue>();
+                }
+                else if (_copy == null)
+                {
+                    _copy = new Dictionary<TKey, TValue>(_source, _source.Comparer);
+                }
+
+                return _copy;
+            }
+        }
+
+        public Dictionary<TKey, TValue>.KeyCollection Keys
+        {
+            get
+            {
+                return ReadDictionary.Keys;
+            }
+        }
+
+        public Dictionary<TKey, TValue>.ValueCollection Values
+        {
+            get
+            {
+                return ReadDictionary.Values;
+            }
+        }
+
+        public int Count
+        {
+            get
+            {
+                return ReadDictionary.Count;
+            }
+        }
+
+        public bool IsReadOnly
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        public TValue this[TKey key]
+        {
+            get
+            {
+                return ReadDictionary[key];
+            }
+            set
+            {
+                WriteDictionary[key] = value;
+            }
+        }
+
+        public bool ContainsKey(TKey key)
+        {
+            return ReadDictionary.ContainsKey(key);
+        }
+
+        public void Add(TKey key, TValue value)
+        {
+            WriteDictionary.Add(key, value);
+        }
+
+        public bool Remove(TKey key)
+        {
+            return WriteDictionary.Remove(key);
+        }
+
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            return ReadDictionary.TryGetValue(key, out value);
+        }
+
+        public void Add(KeyValuePair<TKey, TValue> item)
+        {
+            ((ICollection<KeyValuePair<TKey, TValue>>)WriteDictionary).Add(item);
+        }
+
+        public void Clear()
+        {
+            WriteDictionary.Clear();
+        }
+
+        public bool Contains(KeyValuePair<TKey, TValue> item)
+        {
+            return ((ICollection<KeyValuePair<TKey, TValue>>)ReadDictionary).Contains(item);
+        }
+
+        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+        {
+            ((ICollection<KeyValuePair<TKey, TValue>>)ReadDictionary).CopyTo(array, arrayIndex);
+        }
+
+        public bool Remove(KeyValuePair<TKey, TValue> item)
+        {
+            return ((ICollection<KeyValuePair<TKey, TValue>>)WriteDictionary).Remove(item);
+        }
+
+        public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
+        {
+            return ReadDictionary.GetEnumerator();
+        }
+    }
+}

+ 127 - 0
src/Shared/ObjectMethodExecutor/AwaitableInfo.cs

@@ -0,0 +1,127 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal struct AwaitableInfo
+    {
+        public Type AwaiterType { get; }
+        public PropertyInfo AwaiterIsCompletedProperty { get; }
+        public MethodInfo AwaiterGetResultMethod { get; }
+        public MethodInfo AwaiterOnCompletedMethod { get; }
+        public MethodInfo AwaiterUnsafeOnCompletedMethod { get; }
+        public Type ResultType { get; }
+        public MethodInfo GetAwaiterMethod { get; }
+
+        public AwaitableInfo(
+            Type awaiterType,
+            PropertyInfo awaiterIsCompletedProperty,
+            MethodInfo awaiterGetResultMethod,
+            MethodInfo awaiterOnCompletedMethod,
+            MethodInfo awaiterUnsafeOnCompletedMethod,
+            Type resultType,
+            MethodInfo getAwaiterMethod)
+        {
+            AwaiterType = awaiterType;
+            AwaiterIsCompletedProperty = awaiterIsCompletedProperty;
+            AwaiterGetResultMethod = awaiterGetResultMethod;
+            AwaiterOnCompletedMethod = awaiterOnCompletedMethod;
+            AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod;
+            ResultType = resultType;
+            GetAwaiterMethod = getAwaiterMethod;
+        }
+
+        public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo)
+        {
+            // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347
+
+            // Awaitable must have method matching "object GetAwaiter()"
+            var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m =>
+                m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase)
+                && m.GetParameters().Length == 0
+                && m.ReturnType != null);
+            if (getAwaiterMethod == null)
+            {
+                awaitableInfo = default(AwaitableInfo);
+                return false;
+            }
+
+            var awaiterType = getAwaiterMethod.ReturnType;
+
+            // Awaiter must have property matching "bool IsCompleted { get; }"
+            var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p =>
+                p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase)
+                && p.PropertyType == typeof(bool)
+                && p.GetMethod != null);
+            if (isCompletedProperty == null)
+            {
+                awaitableInfo = default(AwaitableInfo);
+                return false;
+            }
+
+            // Awaiter must implement INotifyCompletion
+            var awaiterInterfaces = awaiterType.GetInterfaces();
+            var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion));
+            if (!implementsINotifyCompletion)
+            {
+                awaitableInfo = default(AwaitableInfo);
+                return false;
+            }
+
+            // INotifyCompletion supplies a method matching "void OnCompleted(Action action)"
+            var iNotifyCompletionMap = awaiterType
+                .GetTypeInfo()
+                .GetRuntimeInterfaceMap(typeof(INotifyCompletion));
+            var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m =>
+                m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase)
+                && m.ReturnType == typeof(void)
+                && m.GetParameters().Length == 1
+                && m.GetParameters()[0].ParameterType == typeof(Action));
+
+            // Awaiter optionally implements ICriticalNotifyCompletion
+            var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));
+            MethodInfo unsafeOnCompletedMethod;
+            if (implementsICriticalNotifyCompletion)
+            {
+                // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)"
+                var iCriticalNotifyCompletionMap = awaiterType
+                    .GetTypeInfo()
+                    .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion));
+                unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m =>
+                    m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase)
+                    && m.ReturnType == typeof(void)
+                    && m.GetParameters().Length == 1
+                    && m.GetParameters()[0].ParameterType == typeof(Action));
+            }
+            else
+            {
+                unsafeOnCompletedMethod = null;
+            }
+
+            // Awaiter must have method matching "void GetResult" or "T GetResult()"
+            var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m =>
+                m.Name.Equals("GetResult")
+                && m.GetParameters().Length == 0);
+            if (getResultMethod == null)
+            {
+                awaitableInfo = default(AwaitableInfo);
+                return false;
+            }
+
+            awaitableInfo = new AwaitableInfo(
+                awaiterType,
+                isCompletedProperty,
+                getResultMethod,
+                onCompletedMethod,
+                unsafeOnCompletedMethod,
+                getResultMethod.ReturnType,
+                getAwaiterMethod);
+            return true;
+        }
+    }
+}

+ 55 - 0
src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs

@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq.Expressions;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal struct CoercedAwaitableInfo
+    {
+        public AwaitableInfo AwaitableInfo { get; }
+        public Expression CoercerExpression { get; }
+        public Type CoercerResultType { get; }
+        public bool RequiresCoercion => CoercerExpression != null;
+
+        public CoercedAwaitableInfo(AwaitableInfo awaitableInfo)
+        {
+            AwaitableInfo = awaitableInfo;
+            CoercerExpression = null;
+            CoercerResultType = null;
+        }
+
+        public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo)
+        {
+            CoercerExpression = coercerExpression;
+            CoercerResultType = coercerResultType;
+            AwaitableInfo = coercedAwaitableInfo;
+        }
+
+        public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info)
+        {
+            if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo))
+            {
+                info = new CoercedAwaitableInfo(directlyAwaitableInfo);
+                return true;
+            }
+
+            // It's not directly awaitable, but maybe we can coerce it.
+            // Currently we support coercing FSharpAsync<T>.
+            if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type,
+                out var coercerExpression,
+                out var coercerResultType))
+            {
+                if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo))
+                {
+                    info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo);
+                    return true;
+                }
+            }
+
+            info = default(CoercedAwaitableInfo);
+            return false;
+        }
+    }
+}

+ 340 - 0
src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs

@@ -0,0 +1,340 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal class ObjectMethodExecutor
+    {
+        private readonly object[] _parameterDefaultValues;
+        private readonly MethodExecutorAsync _executorAsync;
+        private readonly MethodExecutor _executor;
+
+        private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor =
+            typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] {
+                typeof(object),                 // customAwaitable
+                typeof(Func<object, object>),   // getAwaiterMethod
+                typeof(Func<object, bool>),     // isCompletedMethod
+                typeof(Func<object, object>),   // getResultMethod
+                typeof(Action<object, Action>), // onCompletedMethod
+                typeof(Action<object, Action>)  // unsafeOnCompletedMethod
+            });
+
+        private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues)
+        {
+            if (methodInfo == null)
+            {
+                throw new ArgumentNullException(nameof(methodInfo));
+            }
+
+            MethodInfo = methodInfo;
+            MethodParameters = methodInfo.GetParameters();
+            TargetTypeInfo = targetTypeInfo;
+            MethodReturnType = methodInfo.ReturnType;
+
+            var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo);
+
+            IsMethodAsync = isAwaitable;
+            AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null;
+
+            // Upstream code may prefer to use the sync-executor even for async methods, because if it knows
+            // that the result is a specific Task<T> where T is known, then it can directly cast to that type
+            // and await it without the extra heap allocations involved in the _executorAsync code path.
+            _executor = GetExecutor(methodInfo, targetTypeInfo);
+
+            if (IsMethodAsync)
+            {
+                _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo);
+            }
+
+            _parameterDefaultValues = parameterDefaultValues;
+        }
+
+        private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object[] parameters);
+
+        private delegate object MethodExecutor(object target, object[] parameters);
+
+        private delegate void VoidMethodExecutor(object target, object[] parameters);
+
+        public MethodInfo MethodInfo { get; }
+
+        public ParameterInfo[] MethodParameters { get; }
+
+        public TypeInfo TargetTypeInfo { get; }
+
+        public Type AsyncResultType { get; }
+
+        // This field is made internal set because it is set in unit tests.
+        public Type MethodReturnType { get; internal set; }
+
+        public bool IsMethodAsync { get; }
+
+        public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo)
+        {
+            return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null);
+        }
+
+        public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues)
+        {
+            if (parameterDefaultValues == null)
+            {
+                throw new ArgumentNullException(nameof(parameterDefaultValues));
+            }
+
+            return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues);
+        }
+
+        /// <summary>
+        /// Executes the configured method on <paramref name="target"/>. This can be used whether or not
+        /// the configured method is asynchronous.
+        /// </summary>
+        /// <remarks>
+        /// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than
+        /// ExecuteAsync if you know at compile time what the return type is, because then you can directly
+        /// "await" that value (via a cast), and then the generated code will be able to reference the
+        /// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated
+        /// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at
+        /// compile time what type it would be.
+        /// </remarks>
+        /// <param name="target">The object whose method is to be executed.</param>
+        /// <param name="parameters">Parameters to pass to the method.</param>
+        /// <returns>The method return value.</returns>
+        public object Execute(object target, object[] parameters)
+        {
+            return _executor(target, parameters);
+        }
+
+        /// <summary>
+        /// Executes the configured method on <paramref name="target"/>. This can only be used if the configured
+        /// method is asynchronous.
+        /// </summary>
+        /// <remarks>
+        /// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync,
+        /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
+        /// as compared with using Execute and then using "await" on the result value typecasted to the known
+        /// awaitable type. The possible extra heap allocations are for:
+        /// 
+        /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
+        ///    it's a reference type, and you normally create a new instance per call).
+        /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
+        ///    of it, and if it is, it will have to be boxed so the calling code can reference it as an object).
+        /// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling
+        ///    code doesn't know what type it's going to be).
+        /// </remarks>
+        /// <param name="target">The object whose method is to be executed.</param>
+        /// <param name="parameters">Parameters to pass to the method.</param>
+        /// <returns>An object that you can "await" to get the method return value.</returns>
+        public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object[] parameters)
+        {
+            return _executorAsync(target, parameters);
+        }
+
+        public object GetDefaultValueForParameter(int index)
+        {
+            if (_parameterDefaultValues == null)
+            {
+                throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied.");
+            }
+
+            if (index < 0 || index > MethodParameters.Length - 1)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+
+            return _parameterDefaultValues[index];
+        }
+
+        private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo)
+        {
+            // Parameters to executor
+            var targetParameter = Expression.Parameter(typeof(object), "target");
+            var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
+
+            // Build parameter list
+            var parameters = new List<Expression>();
+            var paramInfos = methodInfo.GetParameters();
+            for (int i = 0; i < paramInfos.Length; i++)
+            {
+                var paramInfo = paramInfos[i];
+                var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
+                var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
+
+                // valueCast is "(Ti) parameters[i]"
+                parameters.Add(valueCast);
+            }
+
+            // Call method
+            var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType());
+            var methodCall = Expression.Call(instanceCast, methodInfo, parameters);
+
+            // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)"
+            // Create function
+            if (methodCall.Type == typeof(void))
+            {
+                var lambda = Expression.Lambda<VoidMethodExecutor>(methodCall, targetParameter, parametersParameter);
+                var voidExecutor = lambda.Compile();
+                return WrapVoidMethod(voidExecutor);
+            }
+            else
+            {
+                // must coerce methodCall to match ActionExecutor signature
+                var castMethodCall = Expression.Convert(methodCall, typeof(object));
+                var lambda = Expression.Lambda<MethodExecutor>(castMethodCall, targetParameter, parametersParameter);
+                return lambda.Compile();
+            }
+        }
+
+        private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor)
+        {
+            return delegate (object target, object[] parameters)
+            {
+                executor(target, parameters);
+                return null;
+            };
+        }
+
+        private static MethodExecutorAsync GetExecutorAsync(
+            MethodInfo methodInfo,
+            TypeInfo targetTypeInfo,
+            CoercedAwaitableInfo coercedAwaitableInfo)
+        {
+            // Parameters to executor
+            var targetParameter = Expression.Parameter(typeof(object), "target");
+            var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
+
+            // Build parameter list
+            var parameters = new List<Expression>();
+            var paramInfos = methodInfo.GetParameters();
+            for (int i = 0; i < paramInfos.Length; i++)
+            {
+                var paramInfo = paramInfos[i];
+                var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
+                var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
+
+                // valueCast is "(Ti) parameters[i]"
+                parameters.Add(valueCast);
+            }
+
+            // Call method
+            var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType());
+            var methodCall = Expression.Call(instanceCast, methodInfo, parameters);
+
+            // Using the method return value, construct an ObjectMethodExecutorAwaitable based on
+            // the info we have about its implementation of the awaitable pattern. Note that all
+            // the funcs/actions we construct here are precompiled, so that only one instance of
+            // each is preserved throughout the lifetime of the ObjectMethodExecutor.
+
+            // var getAwaiterFunc = (object awaitable) =>
+            //     (object)((CustomAwaitableType)awaitable).GetAwaiter();
+            var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable");
+            var awaitableInfo = coercedAwaitableInfo.AwaitableInfo;
+            var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType;
+            var getAwaiterFunc = Expression.Lambda<Func<object, object>>(
+                Expression.Convert(
+                    Expression.Call(
+                        Expression.Convert(customAwaitableParam, postCoercionMethodReturnType),
+                        awaitableInfo.GetAwaiterMethod),
+                    typeof(object)),
+                customAwaitableParam).Compile();
+
+            // var isCompletedFunc = (object awaiter) =>
+            //     ((CustomAwaiterType)awaiter).IsCompleted;
+            var isCompletedParam = Expression.Parameter(typeof(object), "awaiter");
+            var isCompletedFunc = Expression.Lambda<Func<object, bool>>(
+                Expression.MakeMemberAccess(
+                    Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType),
+                    awaitableInfo.AwaiterIsCompletedProperty),
+                isCompletedParam).Compile();
+
+            var getResultParam = Expression.Parameter(typeof(object), "awaiter");
+            Func<object, object> getResultFunc;
+            if (awaitableInfo.ResultType == typeof(void))
+            {
+                // var getResultFunc = (object awaiter) =>
+                // {
+                //     ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions
+                //     return (object)null;
+                // };
+                getResultFunc = Expression.Lambda<Func<object, object>>(
+                    Expression.Block(
+                        Expression.Call(
+                            Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
+                            awaitableInfo.AwaiterGetResultMethod),
+                        Expression.Constant(null)
+                    ),
+                    getResultParam).Compile();
+            }
+            else
+            {
+                // var getResultFunc = (object awaiter) =>
+                //     (object)((CustomAwaiterType)awaiter).GetResult();
+                getResultFunc = Expression.Lambda<Func<object, object>>(
+                    Expression.Convert(
+                        Expression.Call(
+                            Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
+                            awaitableInfo.AwaiterGetResultMethod),
+                        typeof(object)),
+                    getResultParam).Compile();
+            }
+
+            // var onCompletedFunc = (object awaiter, Action continuation) => {
+            //     ((CustomAwaiterType)awaiter).OnCompleted(continuation);
+            // };
+            var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
+            var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
+            var onCompletedFunc = Expression.Lambda<Action<object, Action>>(
+                Expression.Call(
+                    Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType),
+                    awaitableInfo.AwaiterOnCompletedMethod,
+                    onCompletedParam2),
+                onCompletedParam1,
+                onCompletedParam2).Compile();
+
+            Action<object, Action> unsafeOnCompletedFunc = null;
+            if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null)
+            {
+                // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => {
+                //     ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation);
+                // };
+                var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
+                var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
+                unsafeOnCompletedFunc = Expression.Lambda<Action<object, Action>>(
+                    Expression.Call(
+                        Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType),
+                        awaitableInfo.AwaiterUnsafeOnCompletedMethod,
+                        unsafeOnCompletedParam2),
+                    unsafeOnCompletedParam1,
+                    unsafeOnCompletedParam2).Compile();
+            }
+
+            // If we need to pass the method call result through a coercer function to get an
+            // awaitable, then do so.
+            var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion
+                ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall)
+                : (Expression)methodCall;
+
+            // return new ObjectMethodExecutorAwaitable(
+            //     (object)coercedMethodCall,
+            //     getAwaiterFunc,
+            //     isCompletedFunc,
+            //     getResultFunc,
+            //     onCompletedFunc,
+            //     unsafeOnCompletedFunc);
+            var returnValueExpression = Expression.New(
+                _objectMethodExecutorAwaitableConstructor,
+                Expression.Convert(coercedMethodCall, typeof(object)),
+                Expression.Constant(getAwaiterFunc),
+                Expression.Constant(isCompletedFunc),
+                Expression.Constant(getResultFunc),
+                Expression.Constant(onCompletedFunc),
+                Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>)));
+
+            var lambda = Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter);
+            return lambda.Compile();
+        }
+    }
+}

+ 114 - 0
src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs

@@ -0,0 +1,114 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Microsoft.Extensions.Internal
+{
+    /// <summary>
+    /// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync"/> can
+    /// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
+    /// application-defined custom awaitable.
+    /// </summary>
+    internal struct ObjectMethodExecutorAwaitable
+    {
+        private readonly object _customAwaitable;
+        private readonly Func<object, object> _getAwaiterMethod;
+        private readonly Func<object, bool> _isCompletedMethod;
+        private readonly Func<object, object> _getResultMethod;
+        private readonly Action<object, Action> _onCompletedMethod;
+        private readonly Action<object, Action> _unsafeOnCompletedMethod;
+
+        // Perf note: since we're requiring the customAwaitable to be supplied here as an object,
+        // this will trigger a further allocation if it was a value type (i.e., to box it). We can't
+        // fix this by making the customAwaitable type generic, because the calling code typically
+        // does not know the type of the awaitable/awaiter at compile-time anyway.
+        //
+        // However, we could fix it by not passing the customAwaitable here at all, and instead
+        // passing a func that maps directly from the target object (e.g., controller instance),
+        // target method (e.g., action method info), and params array to the custom awaiter in the
+        // GetAwaiter() method below. In effect, by delaying the actual method call until the
+        // upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance.
+        // This optimization is not currently implemented because:
+        // [1] It would make no difference when the awaitable was an object type, which is
+        //     by far the most common scenario (e.g., System.Task<T>).
+        // [2] It would be complex - we'd need some kind of object pool to track all the parameter
+        //     arrays until we needed to use them in GetAwaiter().
+        // We can reconsider this in the future if there's a need to optimize for ValueTask<T>
+        // or other value-typed awaitables.
+
+        public ObjectMethodExecutorAwaitable(
+            object customAwaitable,
+            Func<object, object> getAwaiterMethod,
+            Func<object, bool> isCompletedMethod,
+            Func<object, object> getResultMethod,
+            Action<object, Action> onCompletedMethod,
+            Action<object, Action> unsafeOnCompletedMethod)
+        {
+            _customAwaitable = customAwaitable;
+            _getAwaiterMethod = getAwaiterMethod;
+            _isCompletedMethod = isCompletedMethod;
+            _getResultMethod = getResultMethod;
+            _onCompletedMethod = onCompletedMethod;
+            _unsafeOnCompletedMethod = unsafeOnCompletedMethod;
+        }
+
+        public Awaiter GetAwaiter()
+        {
+            var customAwaiter = _getAwaiterMethod(_customAwaitable);
+            return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod);
+        }
+
+        public struct Awaiter : ICriticalNotifyCompletion
+        {
+            private readonly object _customAwaiter;
+            private readonly Func<object, bool> _isCompletedMethod;
+            private readonly Func<object, object> _getResultMethod;
+            private readonly Action<object, Action> _onCompletedMethod;
+            private readonly Action<object, Action> _unsafeOnCompletedMethod;
+
+            public Awaiter(
+                object customAwaiter,
+                Func<object, bool> isCompletedMethod,
+                Func<object, object> getResultMethod,
+                Action<object, Action> onCompletedMethod,
+                Action<object, Action> unsafeOnCompletedMethod)
+            {
+                _customAwaiter = customAwaiter;
+                _isCompletedMethod = isCompletedMethod;
+                _getResultMethod = getResultMethod;
+                _onCompletedMethod = onCompletedMethod;
+                _unsafeOnCompletedMethod = unsafeOnCompletedMethod;
+            }
+
+            public bool IsCompleted => _isCompletedMethod(_customAwaiter);
+
+            public object GetResult() => _getResultMethod(_customAwaiter);
+
+            public void OnCompleted(Action continuation)
+            {
+                _onCompletedMethod(_customAwaiter, continuation);
+            }
+
+            public void UnsafeOnCompleted(Action continuation)
+            {
+                // If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted.
+                // If not, fall back on using its OnCompleted.
+                //
+                // Why this is safe:
+                // - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it
+                //   needs the execution context to be preserved (which it signals by calling OnCompleted), or
+                //   that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not*
+                //   to preserve and restore the context, so we prefer that where possible.
+                // - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted,
+                //   there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen
+                //   if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to
+                //   pass the call on to the underlying awaitable's OnCompleted method.
+
+                var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod;
+                underlyingMethodToUse(_customAwaiter, continuation);
+            }
+        }
+    }
+}

+ 151 - 0
src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs

@@ -0,0 +1,151 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Extensions.Internal
+{
+    /// <summary>
+    /// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying
+    /// an <see cref="Expression"/> for mapping instances of that type to a C# awaitable.
+    /// </summary>
+    /// <remarks>
+    /// The main design goal here is to avoid taking a compile-time dependency on
+    /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references
+    /// to FSharp types have to be constructed dynamically at runtime.
+    /// </remarks>
+    internal static class ObjectMethodExecutorFSharpSupport
+    {
+        private static object _fsharpValuesCacheLock = new object();
+        private static Assembly _fsharpCoreAssembly;
+        private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod;
+        private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty;
+        private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty;
+
+        public static bool TryBuildCoercerFromFSharpAsyncToAwaitable(
+            Type possibleFSharpAsyncType,
+            out Expression coerceToAwaitableExpression,
+            out Type awaitableType)
+        {
+            var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType
+                ? possibleFSharpAsyncType.GetGenericTypeDefinition()
+                : null;
+
+            if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType))
+            {
+                coerceToAwaitableExpression = null;
+                awaitableType = null;
+                return false;
+            }
+
+            var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single();
+            awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType);
+
+            // coerceToAwaitableExpression = (object fsharpAsync) =>
+            // {
+            //     return (object)FSharpAsync.StartAsTask<TResult>(
+            //         (Microsoft.FSharp.Control.FSharpAsync<TResult>)fsharpAsync,
+            //         FSharpOption<TaskCreationOptions>.None,
+            //         FSharpOption<CancellationToken>.None);
+            // };
+            var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod
+                .MakeGenericMethod(awaiterResultType);
+            var coerceToAwaitableParam = Expression.Parameter(typeof(object));
+            coerceToAwaitableExpression = Expression.Lambda(
+                Expression.Convert(
+                    Expression.Call(
+                        startAsTaskClosedMethod,
+                        Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType),
+                        Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty),
+                        Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)),
+                    typeof(object)),
+                coerceToAwaitableParam);
+
+            return true;
+        }
+
+        private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType)
+        {
+            var typeFullName = possibleFSharpAsyncGenericType?.FullName;
+            if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal))
+            {
+                return false;
+            }
+
+            lock (_fsharpValuesCacheLock)
+            {
+                if (_fsharpCoreAssembly != null)
+                {
+                    // Since we've already found the real FSharpAsync.Core assembly, we just have
+                    // to check that the supplied FSharpAsync`1 type is the one from that assembly.
+                    return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly;
+                }
+                else
+                {
+                    // We'll keep trying to find the FSharp types/values each time any type called
+                    // FSharpAsync`1 is supplied.
+                    return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType);
+                }
+            }
+        }
+
+        private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType)
+        {
+            var assembly = possibleFSharpAsyncGenericType.Assembly;
+            var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1");
+            var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync");
+
+            if (fsharpOptionType == null || fsharpAsyncType == null)
+            {
+                return false;
+            }
+
+            // Get a reference to FSharpOption<TaskCreationOptions>.None
+            var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType
+                .MakeGenericType(typeof(TaskCreationOptions));
+            _fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType
+                .GetTypeInfo()
+                .GetRuntimeProperty("None");
+
+            // Get a reference to FSharpOption<CancellationToken>.None
+            var fsharpOptionOfCancellationTokenType = fsharpOptionType
+                .MakeGenericType(typeof(CancellationToken));
+            _fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType
+                .GetTypeInfo()
+                .GetRuntimeProperty("None");
+
+            // Get a reference to FSharpAsync.StartAsTask<>
+            var fsharpAsyncMethods = fsharpAsyncType
+                .GetRuntimeMethods()
+                .Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal));
+            foreach (var candidateMethodInfo in fsharpAsyncMethods)
+            {
+                var parameters = candidateMethodInfo.GetParameters();
+                if (parameters.Length == 3
+                    && TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType)
+                    && parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType
+                    && parameters[2].ParameterType == fsharpOptionOfCancellationTokenType)
+                {
+                    // This really does look like the correct method (and hence assembly).
+                    _fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo;
+                    _fsharpCoreAssembly = assembly;
+                    break;
+                }
+            }
+
+            return _fsharpCoreAssembly != null;
+        }
+
+        private static bool TypesHaveSameIdentity(Type type1, Type type2)
+        {
+            return type1.Assembly == type2.Assembly
+                && string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal)
+                && string.Equals(type1.Name, type2.Name, StringComparison.Ordinal);
+        }
+    }
+}

+ 113 - 0
src/Shared/Process/ProcessExtensions.cs

@@ -0,0 +1,113 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal static class ProcessExtensions
+    {
+        private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+        private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30);
+
+        public static void KillTree(this Process process)
+        {
+            process.KillTree(_defaultTimeout);
+        }
+
+        public static void KillTree(this Process process, TimeSpan timeout)
+        {
+            string stdout;
+            if (_isWindows)
+            {
+                RunProcessAndWaitForExit(
+                    "taskkill",
+                    $"/T /F /PID {process.Id}",
+                    timeout,
+                    out stdout);
+            }
+            else
+            {
+                var children = new HashSet<int>();
+                GetAllChildIdsUnix(process.Id, children, timeout);
+                foreach (var childId in children)
+                {
+                    KillProcessUnix(childId, timeout);
+                }
+                KillProcessUnix(process.Id, timeout);
+            }
+        }
+
+        private static void GetAllChildIdsUnix(int parentId, ISet<int> children, TimeSpan timeout)
+        {
+            string stdout;
+            var exitCode = RunProcessAndWaitForExit(
+                "pgrep",
+                $"-P {parentId}",
+                timeout,
+                out stdout);
+
+            if (exitCode == 0 && !string.IsNullOrEmpty(stdout))
+            {
+                using (var reader = new StringReader(stdout))
+                {
+                    while (true)
+                    {
+                        var text = reader.ReadLine();
+                        if (text == null)
+                        {
+                            return;
+                        }
+
+                        int id;
+                        if (int.TryParse(text, out id))
+                        {
+                            children.Add(id);
+                            // Recursively get the children
+                            GetAllChildIdsUnix(id, children, timeout);
+                        }
+                    }
+                }
+            }
+        }
+
+        private static void KillProcessUnix(int processId, TimeSpan timeout)
+        {
+            string stdout;
+            RunProcessAndWaitForExit(
+                "kill",
+                $"-TERM {processId}",
+                timeout,
+                out stdout);
+        }
+
+        private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout)
+        {
+            var startInfo = new ProcessStartInfo
+            {
+                FileName = fileName,
+                Arguments = arguments,
+                RedirectStandardOutput = true,
+                UseShellExecute = false
+            };
+
+            var process = Process.Start(startInfo);
+
+            stdout = null;
+            if (process.WaitForExit((int)timeout.TotalMilliseconds))
+            {
+                stdout = process.StandardOutput.ReadToEnd();
+            }
+            else
+            {
+                process.Kill();
+            }
+
+            return process.ExitCode;
+        }
+    }
+}

+ 110 - 0
src/Shared/PropertyActivator/PropertyActivator.cs

@@ -0,0 +1,110 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal class PropertyActivator<TContext>
+    {
+        private readonly Func<TContext, object> _valueAccessor;
+        private readonly Action<object, object> _fastPropertySetter;
+
+        public PropertyActivator(
+            PropertyInfo propertyInfo,
+            Func<TContext, object> valueAccessor)
+        {
+            if (propertyInfo == null)
+            {
+                throw new ArgumentNullException(nameof(propertyInfo));
+            }
+
+            if (valueAccessor == null)
+            {
+                throw new ArgumentNullException(nameof(valueAccessor));
+            }
+
+            PropertyInfo = propertyInfo;
+            _valueAccessor = valueAccessor;
+            _fastPropertySetter = PropertyHelper.MakeFastPropertySetter(propertyInfo);
+        }
+
+        public PropertyInfo PropertyInfo { get; private set; }
+
+        public object Activate(object instance, TContext context)
+        {
+            if (instance == null)
+            {
+                throw new ArgumentNullException(nameof(instance));
+            }
+
+            var value = _valueAccessor(context);
+            _fastPropertySetter(instance, value);
+            return value;
+        }
+
+        public static PropertyActivator<TContext>[] GetPropertiesToActivate(
+            Type type,
+            Type activateAttributeType,
+            Func<PropertyInfo, PropertyActivator<TContext>> createActivateInfo)
+        {
+            if (type == null)
+            {
+                throw new ArgumentNullException(nameof(type));
+            }
+
+            if (activateAttributeType == null)
+            {
+                throw new ArgumentNullException(nameof(activateAttributeType));
+            }
+
+            if (createActivateInfo == null)
+            {
+                throw new ArgumentNullException(nameof(createActivateInfo));
+            }
+
+            return GetPropertiesToActivate(type, activateAttributeType, createActivateInfo, includeNonPublic: false);
+        }
+
+        public static PropertyActivator<TContext>[] GetPropertiesToActivate(
+            Type type,
+            Type activateAttributeType,
+            Func<PropertyInfo, PropertyActivator<TContext>> createActivateInfo,
+            bool includeNonPublic)
+        {
+            if (type == null)
+            {
+                throw new ArgumentNullException(nameof(type));
+            }
+
+            if (activateAttributeType == null)
+            {
+                throw new ArgumentNullException(nameof(activateAttributeType));
+            }
+
+            if (createActivateInfo == null)
+            {
+                throw new ArgumentNullException(nameof(createActivateInfo));
+            }
+
+            var properties = type.GetRuntimeProperties()
+                .Where((property) =>
+                {
+                    return
+                        property.IsDefined(activateAttributeType) &&
+                        property.GetIndexParameters().Length == 0 &&
+                        property.SetMethod != null &&
+                        !property.SetMethod.IsStatic;
+                });
+
+            if (!includeNonPublic)
+            {
+                properties = properties.Where(property => property.SetMethod.IsPublic);
+            }
+
+            return properties.Select(createActivateInfo).ToArray();
+        }
+    }
+}

+ 526 - 0
src/Shared/PropertyHelper/PropertyHelper.cs

@@ -0,0 +1,526 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+    internal class PropertyHelper
+    {
+        // Delegate type for a by-ref property getter
+        private delegate TValue ByRefFunc<TDeclaringType, TValue>(ref TDeclaringType arg);
+
+        private static readonly MethodInfo CallPropertyGetterOpenGenericMethod =
+            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter));
+
+        private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod =
+            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference));
+
+        private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod =
+            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter));
+
+        private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod =
+            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference));
+
+        private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
+            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter));
+
+        // Using an array rather than IEnumerable, as target will be called on the hot path numerous times.
+        private static readonly ConcurrentDictionary<Type, PropertyHelper[]> PropertiesCache =
+            new ConcurrentDictionary<Type, PropertyHelper[]>();
+
+        private static readonly ConcurrentDictionary<Type, PropertyHelper[]> VisiblePropertiesCache =
+            new ConcurrentDictionary<Type, PropertyHelper[]>();
+
+        private Action<object, object> _valueSetter;
+        private Func<object, object> _valueGetter;
+
+        /// <summary>
+        /// Initializes a fast <see cref="PropertyHelper"/>.
+        /// This constructor does not cache the helper. For caching, use <see cref="GetProperties(Type)"/>.
+        /// </summary>
+        public PropertyHelper(PropertyInfo property)
+        {
+            if (property == null)
+            {
+                throw new ArgumentNullException(nameof(property));
+            }
+
+            Property = property;
+            Name = property.Name;
+        }
+
+        /// <summary>
+        /// Gets the backing <see cref="PropertyInfo"/>.
+        /// </summary>
+        public PropertyInfo Property { get; }
+
+        /// <summary>
+        /// Gets (or sets in derived types) the property name.
+        /// </summary>
+        public virtual string Name { get; protected set; }
+
+        /// <summary>
+        /// Gets the property value getter.
+        /// </summary>
+        public Func<object, object> ValueGetter
+        {
+            get
+            {
+                if (_valueGetter == null)
+                {
+                    _valueGetter = MakeFastPropertyGetter(Property);
+                }
+
+                return _valueGetter;
+            }
+        }
+
+        /// <summary>
+        /// Gets the property value setter.
+        /// </summary>
+        public Action<object, object> ValueSetter
+        {
+            get
+            {
+                if (_valueSetter == null)
+                {
+                    _valueSetter = MakeFastPropertySetter(Property);
+                }
+
+                return _valueSetter;
+            }
+        }
+
+        /// <summary>
+        /// Returns the property value for the specified <paramref name="instance"/>.
+        /// </summary>
+        /// <param name="instance">The object whose property value will be returned.</param>
+        /// <returns>The property value.</returns>
+        public object GetValue(object instance)
+        {
+            return ValueGetter(instance);
+        }
+
+        /// <summary>
+        /// Sets the property value for the specified <paramref name="instance" />.
+        /// </summary>
+        /// <param name="instance">The object whose property value will be set.</param>
+        /// <param name="value">The property value.</param>
+        public void SetValue(object instance, object value)
+        {
+            ValueSetter(instance, value);
+        }
+
+        /// <summary>
+        /// Creates and caches fast property helpers that expose getters for every public get property on the
+        /// underlying type.
+        /// </summary>
+        /// <param name="typeInfo">The type info to extract property accessors for.</param>
+        /// <returns>A cached array of all public properties of the specified type.
+        /// </returns>
+        public static PropertyHelper[] GetProperties(TypeInfo typeInfo)
+        {
+            return GetProperties(typeInfo.AsType());
+        }
+
+        /// <summary>
+        /// Creates and caches fast property helpers that expose getters for every public get property on the
+        /// specified type.
+        /// </summary>
+        /// <param name="type">The type to extract property accessors for.</param>
+        /// <returns>A cached array of all public properties of the specified type.
+        /// </returns>
+        public static PropertyHelper[] GetProperties(Type type)
+        {
+            return GetProperties(type, CreateInstance, PropertiesCache);
+        }
+
+        /// <summary>
+        /// <para>
+        /// Creates and caches fast property helpers that expose getters for every non-hidden get property
+        /// on the specified type.
+        /// </para>
+        /// <para>
+        /// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
+        /// hidden by definitions using the <c>new</c> keyword.
+        /// </para>
+        /// </summary>
+        /// <param name="typeInfo">The type info to extract property accessors for.</param>
+        /// <returns>
+        /// A cached array of all public properties of the specified type.
+        /// </returns>
+        public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo)
+        {
+            return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache);
+        }
+
+        /// <summary>
+        /// <para>
+        /// Creates and caches fast property helpers that expose getters for every non-hidden get property
+        /// on the specified type.
+        /// </para>
+        /// <para>
+        /// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
+        /// hidden by definitions using the <c>new</c> keyword.
+        /// </para>
+        /// </summary>
+        /// <param name="type">The type to extract property accessors for.</param>
+        /// <returns>
+        /// A cached array of all public properties of the specified type.
+        /// </returns>
+        public static PropertyHelper[] GetVisibleProperties(Type type)
+        {
+            return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache);
+        }
+
+        /// <summary>
+        /// Creates a single fast property getter. The result is not cached.
+        /// </summary>
+        /// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
+        /// <returns>a fast getter.</returns>
+        /// <remarks>
+        /// This method is more memory efficient than a dynamically compiled lambda, and about the
+        /// same speed.
+        /// </remarks>
+        public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
+        {
+            Debug.Assert(propertyInfo != null);
+
+            return MakeFastPropertyGetter(
+                propertyInfo,
+                CallPropertyGetterOpenGenericMethod,
+                CallPropertyGetterByReferenceOpenGenericMethod);
+        }
+
+        /// <summary>
+        /// Creates a single fast property getter which is safe for a null input object. The result is not cached.
+        /// </summary>
+        /// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
+        /// <returns>a fast getter.</returns>
+        /// <remarks>
+        /// This method is more memory efficient than a dynamically compiled lambda, and about the
+        /// same speed.
+        /// </remarks>
+        public static Func<object, object> MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo)
+        {
+            Debug.Assert(propertyInfo != null);
+
+            return MakeFastPropertyGetter(
+                propertyInfo,
+                CallNullSafePropertyGetterOpenGenericMethod,
+                CallNullSafePropertyGetterByReferenceOpenGenericMethod);
+        }
+
+        private static Func<object, object> MakeFastPropertyGetter(
+            PropertyInfo propertyInfo,
+            MethodInfo propertyGetterWrapperMethod,
+            MethodInfo propertyGetterByRefWrapperMethod)
+        {
+            Debug.Assert(propertyInfo != null);
+
+            // Must be a generic method with a Func<,> parameter
+            Debug.Assert(propertyGetterWrapperMethod != null);
+            Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition);
+            Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2);
+
+            // Must be a generic method with a ByRefFunc<,> parameter
+            Debug.Assert(propertyGetterByRefWrapperMethod != null);
+            Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition);
+            Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2);
+
+            var getMethod = propertyInfo.GetMethod;
+            Debug.Assert(getMethod != null);
+            Debug.Assert(!getMethod.IsStatic);
+            Debug.Assert(getMethod.GetParameters().Length == 0);
+
+            // Instance methods in the CLR can be turned into static methods where the first parameter
+            // is open over "target". This parameter is always passed by reference, so we have a code
+            // path for value types and a code path for reference types.
+            if (getMethod.DeclaringType.GetTypeInfo().IsValueType)
+            {
+                // Create a delegate (ref TDeclaringType) -> TValue
+                return MakeFastPropertyGetter(
+                    typeof(ByRefFunc<,>),
+                    getMethod,
+                    propertyGetterByRefWrapperMethod);
+            }
+            else
+            {
+                // Create a delegate TDeclaringType -> TValue
+                return MakeFastPropertyGetter(
+                    typeof(Func<,>),
+                    getMethod,
+                    propertyGetterWrapperMethod);
+            }
+        }
+
+        private static Func<object, object> MakeFastPropertyGetter(
+            Type openGenericDelegateType,
+            MethodInfo propertyGetMethod,
+            MethodInfo openGenericWrapperMethod)
+        {
+            var typeInput = propertyGetMethod.DeclaringType;
+            var typeOutput = propertyGetMethod.ReturnType;
+
+            var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput);
+            var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType);
+
+            var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput);
+            var accessorDelegate = wrapperDelegateMethod.CreateDelegate(
+                typeof(Func<object, object>),
+                propertyGetterDelegate);
+
+            return (Func<object, object>)accessorDelegate;
+        }
+
+        /// <summary>
+        /// Creates a single fast property setter for reference types. The result is not cached.
+        /// </summary>
+        /// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
+        /// <returns>a fast getter.</returns>
+        /// <remarks>
+        /// This method is more memory efficient than a dynamically compiled lambda, and about the
+        /// same speed. This only works for reference types.
+        /// </remarks>
+        public static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
+        {
+            Debug.Assert(propertyInfo != null);
+            Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType);
+
+            var setMethod = propertyInfo.SetMethod;
+            Debug.Assert(setMethod != null);
+            Debug.Assert(!setMethod.IsStatic);
+            Debug.Assert(setMethod.ReturnType == typeof(void));
+            var parameters = setMethod.GetParameters();
+            Debug.Assert(parameters.Length == 1);
+
+            // Instance methods in the CLR can be turned into static methods where the first parameter
+            // is open over "target". This parameter is always passed by reference, so we have a code
+            // path for value types and a code path for reference types.
+            var typeInput = setMethod.DeclaringType;
+            var parameterType = parameters[0].ParameterType;
+
+            // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
+            var propertySetterAsAction =
+                setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
+            var callPropertySetterClosedGenericMethod =
+                CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
+            var callPropertySetterDelegate =
+                callPropertySetterClosedGenericMethod.CreateDelegate(
+                    typeof(Action<object, object>), propertySetterAsAction);
+
+            return (Action<object, object>)callPropertySetterDelegate;
+        }
+
+        /// <summary>
+        /// Given an object, adds each instance property with a public get method as a key and its
+        /// associated value to a dictionary.
+        ///
+        /// If the object is already an <see cref="IDictionary{String, Object}"/> instance, then a copy
+        /// is returned.
+        /// </summary>
+        /// <remarks>
+        /// The implementation of PropertyHelper will cache the property accessors per-type. This is
+        /// faster when the same type is used multiple times with ObjectToDictionary.
+        /// </remarks>
+        public static IDictionary<string, object> ObjectToDictionary(object value)
+        {
+            var dictionary = value as IDictionary<string, object>;
+            if (dictionary != null)
+            {
+                return new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase);
+            }
+
+            dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+
+            if (value != null)
+            {
+                foreach (var helper in GetProperties(value.GetType()))
+                {
+                    dictionary[helper.Name] = helper.GetValue(value);
+                }
+            }
+
+            return dictionary;
+        }
+
+        private static PropertyHelper CreateInstance(PropertyInfo property)
+        {
+            return new PropertyHelper(property);
+        }
+
+        // Called via reflection
+        private static object CallPropertyGetter<TDeclaringType, TValue>(
+            Func<TDeclaringType, TValue> getter,
+            object target)
+        {
+            return getter((TDeclaringType)target);
+        }
+
+        // Called via reflection
+        private static object CallPropertyGetterByReference<TDeclaringType, TValue>(
+            ByRefFunc<TDeclaringType, TValue> getter,
+            object target)
+        {
+            var unboxed = (TDeclaringType)target;
+            return getter(ref unboxed);
+        }
+
+        // Called via reflection
+        private static object CallNullSafePropertyGetter<TDeclaringType, TValue>(
+            Func<TDeclaringType, TValue> getter,
+            object target)
+        {
+            if (target == null)
+            {
+                return null;
+            }
+
+            return getter((TDeclaringType)target);
+        }
+
+        // Called via reflection
+        private static object CallNullSafePropertyGetterByReference<TDeclaringType, TValue>(
+            ByRefFunc<TDeclaringType, TValue> getter,
+            object target)
+        {
+            if (target == null)
+            {
+                return null;
+            }
+
+            var unboxed = (TDeclaringType)target;
+            return getter(ref unboxed);
+        }
+
+        private static void CallPropertySetter<TDeclaringType, TValue>(
+            Action<TDeclaringType, TValue> setter,
+            object target,
+            object value)
+        {
+            setter((TDeclaringType)target, (TValue)value);
+        }
+
+        protected static PropertyHelper[] GetVisibleProperties(
+            Type type,
+            Func<PropertyInfo, PropertyHelper> createPropertyHelper,
+            ConcurrentDictionary<Type, PropertyHelper[]> allPropertiesCache,
+            ConcurrentDictionary<Type, PropertyHelper[]> visiblePropertiesCache)
+        {
+            PropertyHelper[] result;
+            if (visiblePropertiesCache.TryGetValue(type, out result))
+            {
+                return result;
+            }
+
+            // The simple and common case, this is normal POCO object - no need to allocate.
+            var allPropertiesDefinedOnType = true;
+            var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache);
+            foreach (var propertyHelper in allProperties)
+            {
+                if (propertyHelper.Property.DeclaringType != type)
+                {
+                    allPropertiesDefinedOnType = false;
+                    break;
+                }
+            }
+
+            if (allPropertiesDefinedOnType)
+            {
+                result = allProperties;
+                visiblePropertiesCache.TryAdd(type, result);
+                return result;
+            }
+
+            // There's some inherited properties here, so we need to check for hiding via 'new'.
+            var filteredProperties = new List<PropertyHelper>(allProperties.Length);
+            foreach (var propertyHelper in allProperties)
+            {
+                var declaringType = propertyHelper.Property.DeclaringType;
+                if (declaringType == type)
+                {
+                    filteredProperties.Add(propertyHelper);
+                    continue;
+                }
+
+                // If this property was declared on a base type then look for the definition closest to the
+                // the type to see if we should include it.
+                var ignoreProperty = false;
+
+                // Walk up the hierarchy until we find the type that actually declares this
+                // PropertyInfo.
+                var currentTypeInfo = type.GetTypeInfo();
+                var declaringTypeInfo = declaringType.GetTypeInfo();
+                while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo)
+                {
+                    // We've found a 'more proximal' public definition
+                    var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name);
+                    if (declaredProperty != null)
+                    {
+                        ignoreProperty = true;
+                        break;
+                    }
+
+                    currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo();
+                }
+
+                if (!ignoreProperty)
+                {
+                    filteredProperties.Add(propertyHelper);
+                }
+            }
+
+            result = filteredProperties.ToArray();
+            visiblePropertiesCache.TryAdd(type, result);
+            return result;
+        }
+
+        protected static PropertyHelper[] GetProperties(
+            Type type,
+            Func<PropertyInfo, PropertyHelper> createPropertyHelper,
+            ConcurrentDictionary<Type, PropertyHelper[]> cache)
+        {
+            // Unwrap nullable types. This means Nullable<T>.Value and Nullable<T>.HasValue will not be
+            // part of the sequence of properties returned by this method.
+            type = Nullable.GetUnderlyingType(type) ?? type;
+
+            PropertyHelper[] helpers;
+            if (!cache.TryGetValue(type, out helpers))
+            {
+                // We avoid loading indexed properties using the Where statement.
+                var properties = type.GetRuntimeProperties().Where(IsInterestingProperty);
+
+                var typeInfo = type.GetTypeInfo();
+                if (typeInfo.IsInterface)
+                {
+                    // Reflection does not return information about inherited properties on the interface itself.
+                    properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany(
+                        interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty)));
+                }
+
+                helpers = properties.Select(p => createPropertyHelper(p)).ToArray();
+                cache.TryAdd(type, helpers);
+            }
+
+            return helpers;
+        }
+
+        // Indexed properties are not useful (or valid) for grabbing properties off an object.
+        private static bool IsInterestingProperty(PropertyInfo property)
+        {
+            // For improving application startup time, do not use GetIndexParameters() api early in this check as it
+            // creates a copy of parameter array and also we would like to check for the presence of a get method
+            // and short circuit asap.
+            return property.GetMethod != null &&
+                property.GetMethod.IsPublic &&
+                !property.GetMethod.IsStatic &&
+                property.GetMethod.GetParameters().Length == 0;
+        }
+    }
+}

+ 38 - 0
src/Shared/RazorViews/AttributeValue.cs

@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.Extensions.RazorViews
+{
+    internal class AttributeValue
+    {
+        public AttributeValue(string prefix, object value, bool literal)
+        {
+            Prefix = prefix;
+            Value = value;
+            Literal = literal;
+        }
+
+        public string Prefix { get; }
+
+        public object Value { get; }
+
+        public bool Literal { get; }
+
+        public static AttributeValue FromTuple(Tuple<string, object, bool> value)
+        {
+            return new AttributeValue(value.Item1, value.Item2, value.Item3);
+        }
+
+        public static AttributeValue FromTuple(Tuple<string, string, bool> value)
+        {
+            return new AttributeValue(value.Item1, value.Item2, value.Item3);
+        }
+
+        public static implicit operator AttributeValue(Tuple<string, object, bool> value)
+        {
+            return FromTuple(value);
+        }
+    }
+}

+ 279 - 0
src/Shared/RazorViews/BaseView.cs

@@ -0,0 +1,279 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.Extensions.RazorViews
+{
+    /// <summary>
+    /// Infrastructure
+    /// </summary>
+    internal abstract class BaseView
+    {
+        private static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
+        private readonly Stack<TextWriter> _textWriterStack = new Stack<TextWriter>();
+
+        /// <summary>
+        /// The request context
+        /// </summary>
+        protected HttpContext Context { get; private set; }
+
+        /// <summary>
+        /// The request
+        /// </summary>
+        protected HttpRequest Request { get; private set; }
+
+        /// <summary>
+        /// The response
+        /// </summary>
+        protected HttpResponse Response { get; private set; }
+
+        /// <summary>
+        /// The output stream
+        /// </summary>
+        protected TextWriter Output { get; private set; }
+
+        /// <summary>
+        /// Html encoder used to encode content.
+        /// </summary>
+        protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default;
+
+        /// <summary>
+        /// Url encoder used to encode content.
+        /// </summary>
+        protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default;
+
+        /// <summary>
+        /// JavaScript encoder used to encode content.
+        /// </summary>
+        protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;
+
+        /// <summary>
+        /// Execute an individual request
+        /// </summary>
+        /// <param name="context"></param>
+        public async Task ExecuteAsync(HttpContext context)
+        {
+            Context = context;
+            Request = Context.Request;
+            Response = Context.Response;
+            Output = new StreamWriter(Response.Body, UTF8NoBOM, 4096, leaveOpen: true);
+            await ExecuteAsync();
+            Output.Dispose();
+        }
+
+        /// <summary>
+        /// Execute an individual request
+        /// </summary>
+        public abstract Task ExecuteAsync();
+
+        protected virtual void PushWriter(TextWriter writer)
+        {
+            if (writer == null)
+            {
+                throw new ArgumentNullException(nameof(writer));
+            }
+
+            _textWriterStack.Push(Output);
+            Output = writer;
+        }
+
+        protected virtual TextWriter PopWriter()
+        {
+            Output = _textWriterStack.Pop();
+            return Output;
+        }
+
+        /// <summary>
+        /// Write the given value without HTML encoding directly to <see cref="Output"/>.
+        /// </summary>
+        /// <param name="value">The <see cref="object"/> to write.</param>
+        protected void WriteLiteral(object value)
+        {
+            WriteLiteral(Convert.ToString(value, CultureInfo.InvariantCulture));
+        }
+
+        /// <summary>
+        /// Write the given value without HTML encoding directly to <see cref="Output"/>.
+        /// </summary>
+        /// <param name="value">The <see cref="string"/> to write.</param>
+        protected void WriteLiteral(string value)
+        {
+            if (!string.IsNullOrEmpty(value))
+            {
+                Output.Write(value);
+            }
+        }
+
+        private List<string> AttributeValues { get; set; }
+
+        protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno)
+        {
+            if (AttributeValues == null)
+            {
+                AttributeValues = new List<string>();
+            }
+
+            AttributeValues.Add(value.ToString());
+        }
+
+        private string AttributeEnding { get; set; }
+
+        protected void BeginWriteAttribute(string name, string begining, int startPosition, string ending, int endPosition, int thingy)
+        {
+            Debug.Assert(string.IsNullOrEmpty(AttributeEnding));
+
+            Output.Write(begining);
+            AttributeEnding = ending;
+        }
+
+        protected void EndWriteAttribute()
+        {
+            Debug.Assert(!string.IsNullOrEmpty(AttributeEnding));
+
+            var attributes = string.Join(" ", AttributeValues);
+            Output.Write(attributes);
+            AttributeValues = null;
+
+            Output.Write(AttributeEnding);
+            AttributeEnding = null;
+        }
+
+        /// <summary>
+        /// Writes the given attribute to the given writer
+        /// </summary>
+        /// <param name="name">The name of the attribute to write</param>
+        /// <param name="leader">The value of the prefix</param>
+        /// <param name="trailer">The value of the suffix</param>
+        /// <param name="values">The <see cref="AttributeValue"/>s to write.</param>
+        protected void WriteAttribute(
+            string name,
+            string leader,
+            string trailer,
+            params AttributeValue[] values)
+        {
+            if (name == null)
+            {
+                throw new ArgumentNullException(nameof(name));
+            }
+
+            if (leader == null)
+            {
+                throw new ArgumentNullException(nameof(leader));
+            }
+
+            if (trailer == null)
+            {
+                throw new ArgumentNullException(nameof(trailer));
+            }
+
+            WriteLiteral(leader);
+            foreach (var value in values)
+            {
+                WriteLiteral(value.Prefix);
+
+                // The special cases here are that the value we're writing might already be a string, or that the
+                // value might be a bool. If the value is the bool 'true' we want to write the attribute name
+                // instead of the string 'true'. If the value is the bool 'false' we don't want to write anything.
+                // Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself.
+                string stringValue;
+                if (value.Value is bool)
+                {
+                    if ((bool)value.Value)
+                    {
+                        stringValue = name;
+                    }
+                    else
+                    {
+                        continue;
+                    }
+                }
+                else
+                {
+                    stringValue = value.Value as string;
+                }
+
+                // Call the WriteTo(string) overload when possible
+                if (value.Literal && stringValue != null)
+                {
+                    WriteLiteral(stringValue);
+                }
+                else if (value.Literal)
+                {
+                    WriteLiteral(value.Value);
+                }
+                else if (stringValue != null)
+                {
+                    Write(stringValue);
+                }
+                else
+                {
+                    Write(value.Value);
+                }
+            }
+            WriteLiteral(trailer);
+        }
+
+        /// <summary>
+        /// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked
+        /// </summary>
+        /// <param name="result">The <see cref="HelperResult"/> to invoke</param>
+        protected void Write(HelperResult result)
+        {
+            Write(result);
+        }
+
+        /// <summary>
+        /// Writes the specified <paramref name="value"/> to <see cref="Output"/>.
+        /// </summary>
+        /// <param name="value">The <see cref="object"/> to write.</param>
+        /// <remarks>
+        /// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked for <see cref="HelperResult"/> types.
+        /// For all other types, the encoded result of <see cref="object.ToString"/> is written to
+        /// <see cref="Output"/>.
+        /// </remarks>
+        protected void Write(object value)
+        {
+            if (value is HelperResult helperResult)
+            {
+                helperResult.WriteTo(Output);
+            }
+            else
+            {
+                Write(Convert.ToString(value, CultureInfo.InvariantCulture));
+            }
+        }
+
+        /// <summary>
+        /// Writes the specified <paramref name="value"/> with HTML encoding to <see cref="Output"/>.
+        /// </summary>
+        /// <param name="value">The <see cref="string"/> to write.</param>
+        protected void Write(string value)
+        {
+            WriteLiteral(HtmlEncoder.Encode(value));
+        }
+
+        protected string HtmlEncodeAndReplaceLineBreaks(string input)
+        {
+            if (string.IsNullOrEmpty(input))
+            {
+                return string.Empty;
+            }
+
+            // Split on line breaks before passing it through the encoder.
+            return string.Join("<br />" + Environment.NewLine,
+                input.Split(new[] { "\r\n" }, StringSplitOptions.None)
+                .SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None))
+                .Select(HtmlEncoder.Encode));
+        }
+    }
+}

+ 34 - 0
src/Shared/RazorViews/HelperResult.cs

@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+
+namespace Microsoft.Extensions.RazorViews
+{
+    /// <summary>
+    /// Represents a deferred write operation in a <see cref="BaseView"/>.
+    /// </summary>
+    internal class HelperResult
+    {
+        /// <summary>
+        /// Creates a new instance of <see cref="HelperResult"/>.
+        /// </summary>
+        /// <param name="action">The delegate to invoke when <see cref="WriteTo(TextWriter)"/> is called.</param>
+        public HelperResult(Action<TextWriter> action)
+        {
+            WriteAction = action;
+        }
+
+        public Action<TextWriter> WriteAction { get; }
+
+        /// <summary>
+        /// Method invoked to produce content from the <see cref="HelperResult"/>.
+        /// </summary>
+        /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+        public void WriteTo(TextWriter writer)
+        {
+            WriteAction(writer);
+        }
+    }
+}

+ 40 - 0
src/Shared/SecurityHelper/SecurityHelper.cs

@@ -0,0 +1,40 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Security.Claims;
+
+namespace Microsoft.Extensions.Internal
+{
+    /// <summary>
+    /// Helper code used when implementing authentication middleware
+    /// </summary>
+    internal static class SecurityHelper
+    {
+        /// <summary>
+        /// Add all ClaimsIdentities from an additional ClaimPrincipal to the ClaimsPrincipal
+        /// Merges a new claims principal, placing all new identities first, and eliminating
+        /// any empty unauthenticated identities from context.User
+        /// </summary>
+        /// <param name="existingPrincipal">The <see cref="ClaimsPrincipal"/> containing existing <see cref="ClaimsIdentity"/>.</param>
+        /// <param name="additionalPrincipal">The <see cref="ClaimsPrincipal"/> containing <see cref="ClaimsIdentity"/> to be added.</param>
+        public static ClaimsPrincipal MergeUserPrincipal(ClaimsPrincipal existingPrincipal, ClaimsPrincipal additionalPrincipal)
+        {
+            var newPrincipal = new ClaimsPrincipal();
+
+            // New principal identities go first
+            if (additionalPrincipal != null)
+            {
+                newPrincipal.AddIdentities(additionalPrincipal.Identities);
+            }
+
+            // Then add any existing non empty or authenticated identities
+            if (existingPrincipal != null)
+            {
+                newPrincipal.AddIdentities(existingPrincipal.Identities.Where(i => i.IsAuthenticated || i.Claims.Any()));
+            }
+            return newPrincipal;
+        }
+    }
+}

+ 29 - 0
src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs

@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    /// <summary>
+    /// Contains details for individual exception messages.
+    /// </summary>
+    internal class ExceptionDetails
+    {
+        /// <summary>
+        /// An individual exception
+        /// </summary>
+        public Exception Error { get; set; }
+
+        /// <summary>
+        /// The generated stack frames
+        /// </summary>
+        public IEnumerable<StackFrameSourceCodeInfo> StackFrames { get; set; }
+
+        /// <summary>
+        /// Gets or sets the summary message.
+        /// </summary>
+        public string ErrorMessage { get; set; }
+    }
+}

+ 170 - 0
src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs

@@ -0,0 +1,170 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Extensions.FileProviders;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class ExceptionDetailsProvider
+    {
+        private readonly IFileProvider _fileProvider;
+        private readonly int _sourceCodeLineCount;
+
+        public ExceptionDetailsProvider(IFileProvider fileProvider, int sourceCodeLineCount)
+        {
+            _fileProvider = fileProvider;
+            _sourceCodeLineCount = sourceCodeLineCount;
+        }
+
+        public IEnumerable<ExceptionDetails> GetDetails(Exception exception)
+        {
+            var exceptions = FlattenAndReverseExceptionTree(exception);
+
+            foreach (var ex in exceptions)
+            {
+                yield return new ExceptionDetails
+                {
+                    Error = ex,
+                    StackFrames = StackTraceHelper.GetFrames(ex)
+                            .Select(frame => GetStackFrameSourceCodeInfo(
+                                frame.MethodDisplayInfo.ToString(),
+                                frame.FilePath,
+                                frame.LineNumber))
+                };
+            }
+        }
+
+        private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception ex)
+        {
+            // ReflectionTypeLoadException is special because the details are in
+            // the LoaderExceptions property
+            var typeLoadException = ex as ReflectionTypeLoadException;
+            if (typeLoadException != null)
+            {
+                var typeLoadExceptions = new List<Exception>();
+                foreach (var loadException in typeLoadException.LoaderExceptions)
+                {
+                    typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException));
+                }
+
+                typeLoadExceptions.Add(ex);
+                return typeLoadExceptions;
+            }
+
+            var list = new List<Exception>();
+            if (ex is AggregateException aggregateException)
+            {
+                list.Add(ex);
+                foreach (var innerException in aggregateException.Flatten().InnerExceptions)
+                {
+                    list.Add(innerException);
+                }
+            }
+
+            else
+            {
+                while (ex != null)
+                {
+                    list.Add(ex);
+                    ex = ex.InnerException;
+                }
+                list.Reverse();
+            }
+
+            return list;
+        }
+
+        // make it internal to enable unit testing
+        internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber)
+        {
+            var stackFrame = new StackFrameSourceCodeInfo
+            {
+                Function = method,
+                File = filePath,
+                Line = lineNumber
+            };
+
+            if (string.IsNullOrEmpty(stackFrame.File))
+            {
+                return stackFrame;
+            }
+
+            IEnumerable<string> lines = null;
+            if (File.Exists(stackFrame.File))
+            {
+                lines = File.ReadLines(stackFrame.File);
+            }
+            else
+            {
+                // Handle relative paths and embedded files
+                var fileInfo = _fileProvider.GetFileInfo(stackFrame.File);
+                if (fileInfo.Exists)
+                {
+                    // ReadLines doesn't accept a stream. Use ReadLines as its more efficient
+                    // relative to reading lines via stream reader
+                    if (!string.IsNullOrEmpty(fileInfo.PhysicalPath))
+                    {
+                        lines = File.ReadLines(fileInfo.PhysicalPath);
+                    }
+                    else
+                    {
+                        lines = ReadLines(fileInfo);
+                    }
+                }
+            }
+
+            if (lines != null)
+            {
+                ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line);
+            }
+
+            return stackFrame;
+        }
+
+        // make it internal to enable unit testing
+        internal void ReadFrameContent(
+            StackFrameSourceCodeInfo frame,
+            IEnumerable<string> allLines,
+            int errorStartLineNumberInFile,
+            int errorEndLineNumberInFile)
+        {
+            // Get the line boundaries in the file to be read and read all these lines at once into an array.
+            var preErrorLineNumberInFile = Math.Max(errorStartLineNumberInFile - _sourceCodeLineCount, 1);
+            var postErrorLineNumberInFile = errorEndLineNumberInFile + _sourceCodeLineCount;
+            var codeBlock = allLines
+                .Skip(preErrorLineNumberInFile - 1)
+                .Take(postErrorLineNumberInFile - preErrorLineNumberInFile + 1)
+                .ToArray();
+
+            var numOfErrorLines = (errorEndLineNumberInFile - errorStartLineNumberInFile) + 1;
+            var errorStartLineNumberInArray = errorStartLineNumberInFile - preErrorLineNumberInFile;
+
+            frame.PreContextLine = preErrorLineNumberInFile;
+            frame.PreContextCode = codeBlock.Take(errorStartLineNumberInArray).ToArray();
+            frame.ContextCode = codeBlock
+                .Skip(errorStartLineNumberInArray)
+                .Take(numOfErrorLines)
+                .ToArray();
+            frame.PostContextCode = codeBlock
+                .Skip(errorStartLineNumberInArray + numOfErrorLines)
+                .ToArray();
+        }
+
+        private static IEnumerable<string> ReadLines(IFileInfo fileInfo)
+        {
+            using (var reader = new StreamReader(fileInfo.CreateReadStream()))
+            {
+                string line;
+                while ((line = reader.ReadLine()) != null)
+                {
+                    yield return line;
+                }
+            }
+        }
+    }
+}

+ 49 - 0
src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs

@@ -0,0 +1,49 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class MethodDisplayInfo
+    {
+        public string DeclaringTypeName { get; set; }
+
+        public string Name { get; set; }
+
+        public string GenericArguments { get; set; }
+
+        public string SubMethod { get; set; }
+
+        public IEnumerable<ParameterDisplayInfo> Parameters { get; set; }
+
+        public override string ToString()
+        {
+            var builder = new StringBuilder();
+            if (!string.IsNullOrEmpty(DeclaringTypeName))
+            {
+                builder
+                    .Append(DeclaringTypeName)
+                    .Append(".");
+            }
+
+            builder.Append(Name);
+            builder.Append(GenericArguments);
+
+            builder.Append("(");
+            builder.Append(string.Join(", ", Parameters.Select(p => p.ToString())));
+            builder.Append(")");
+
+            if (!string.IsNullOrEmpty(SubMethod))
+            {
+                builder.Append("+");
+                builder.Append(SubMethod);
+                builder.Append("()");
+            }
+
+            return builder.ToString();
+        }
+    }
+}

+ 33 - 0
src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs

@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Text;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class ParameterDisplayInfo
+    {
+        public string Name { get; set; }
+
+        public string Type { get; set; }
+
+        public string Prefix { get; set; }
+
+        public override string ToString()
+        {
+            var builder = new StringBuilder();
+            if (!string.IsNullOrEmpty(Prefix))
+            {
+                builder
+                    .Append(Prefix)
+                    .Append(" ");
+            }
+
+            builder.Append(Type);
+            builder.Append(" ");
+            builder.Append(Name);
+
+            return builder.ToString();
+        }
+    }
+}

+ 135 - 0
src/Shared/StackTrace/StackFrame/PortablePdbReader.cs

@@ -0,0 +1,135 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class PortablePdbReader : IDisposable
+    {
+        private readonly Dictionary<string, MetadataReaderProvider> _cache =
+            new Dictionary<string, MetadataReaderProvider>(StringComparer.Ordinal);
+
+        public void PopulateStackFrame(StackFrameInfo frameInfo, MethodBase method, int IlOffset)
+        {
+            if (method.Module.Assembly.IsDynamic)
+            {
+                return;
+            }
+
+            var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
+
+            if (metadataReader == null)
+            {
+                return;
+            }
+
+            var methodToken = MetadataTokens.Handle(method.MetadataToken);
+
+            Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition);
+
+            var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle();
+
+            if (!handle.IsNil)
+            {
+                var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle);
+                var sequencePoints = methodDebugInfo.GetSequencePoints();
+                SequencePoint? bestPointSoFar = null;
+
+                foreach (var point in sequencePoints)
+                {
+                    if (point.Offset > IlOffset)
+                    {
+                        break;
+                    }
+
+                    if (point.StartLine != SequencePoint.HiddenLine)
+                    {
+                        bestPointSoFar = point;
+                    }
+                }
+
+                if (bestPointSoFar.HasValue)
+                {
+                    frameInfo.LineNumber = bestPointSoFar.Value.StartLine;
+                    frameInfo.FilePath = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name);
+                }
+            }
+        }
+
+        private MetadataReader GetMetadataReader(string assemblyPath)
+        {
+            MetadataReaderProvider provider = null;
+            if (!_cache.TryGetValue(assemblyPath, out provider))
+            {
+                var pdbPath = GetPdbPath(assemblyPath);
+
+                if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath))
+                {
+                    var pdbStream = File.OpenRead(pdbPath);
+                    provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+                }
+
+                _cache[assemblyPath] = provider;
+            }
+
+            return provider?.GetMetadataReader();
+        }
+
+        private static string GetPdbPath(string assemblyPath)
+        {
+            if (string.IsNullOrEmpty(assemblyPath))
+            {
+                return null;
+            }
+
+            if (File.Exists(assemblyPath))
+            {
+                var peStream = File.OpenRead(assemblyPath);
+
+                using (var peReader = new PEReader(peStream))
+                {
+                    foreach (var entry in peReader.ReadDebugDirectory())
+                    {
+                        if (entry.Type == DebugDirectoryEntryType.CodeView)
+                        {
+                            var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
+                            var peDirectory = Path.GetDirectoryName(assemblyPath);
+                            return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
+                        }
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private static bool IsPortable(string pdbPath)
+        {
+            using (var pdbStream = File.OpenRead(pdbPath))
+            {
+                return pdbStream.ReadByte() == 'B' &&
+                    pdbStream.ReadByte() == 'S' &&
+                    pdbStream.ReadByte() == 'J' &&
+                    pdbStream.ReadByte() == 'B';
+            }
+        }
+
+        public void Dispose()
+        {
+            foreach (var entry in _cache)
+            {
+                entry.Value?.Dispose();
+            }
+
+            _cache.Clear();
+        }
+    }
+}

+ 18 - 0
src/Shared/StackTrace/StackFrame/StackFrameInfo.cs

@@ -0,0 +1,18 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Diagnostics;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class StackFrameInfo
+    {
+        public int LineNumber { get; set; }
+
+        public string FilePath { get; set; }
+
+        public StackFrame StackFrame { get; set; }
+
+        public MethodDisplayInfo MethodDisplayInfo { get; set; }
+    }
+}

+ 54 - 0
src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs

@@ -0,0 +1,54 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    /// <summary>
+    /// Contains the source code where the exception occurred.
+    /// </summary>
+    internal class StackFrameSourceCodeInfo
+    {
+        /// <summary>
+        /// Function containing instruction
+        /// </summary>
+        public string Function { get; set; }
+
+        /// <summary>
+        /// File containing the instruction
+        /// </summary>
+        public string File { get; set; }
+
+        /// <summary>
+        /// The line number of the instruction
+        /// </summary>
+        public int Line { get; set; }
+
+        /// <summary>
+        /// The line preceding the frame line
+        /// </summary>
+        public int PreContextLine { get; set; }
+
+        /// <summary>
+        /// Lines of code before the actual error line(s).
+        /// </summary>
+        public IEnumerable<string> PreContextCode { get; set; } = Enumerable.Empty<string>();
+
+        /// <summary>
+        /// Line(s) of code responsible for the error.
+        /// </summary>
+        public IEnumerable<string> ContextCode { get; set; } = Enumerable.Empty<string>();
+
+        /// <summary>
+        /// Lines of code after the actual error line(s).
+        /// </summary>
+        public IEnumerable<string> PostContextCode { get; set; } = Enumerable.Empty<string>();
+
+        /// <summary>
+        /// Specific error details for this stack frame.
+        /// </summary>
+        public string ErrorDetails { get; set; }
+    }
+}

+ 261 - 0
src/Shared/StackTrace/StackFrame/StackTraceHelper.cs

@@ -0,0 +1,261 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.Extensions.StackTrace.Sources
+{
+    internal class StackTraceHelper
+    {
+        public static IList<StackFrameInfo> GetFrames(Exception exception)
+        {
+            var frames = new List<StackFrameInfo>();
+
+            if (exception == null)
+            {
+                return frames;
+            }
+
+            using (var portablePdbReader = new PortablePdbReader())
+            {
+                var needFileInfo = true;
+                var stackTrace = new System.Diagnostics.StackTrace(exception, needFileInfo);
+                var stackFrames = stackTrace.GetFrames();
+
+                if (stackFrames == null)
+                {
+                    return frames;
+                }
+
+                for (var i = 0; i < stackFrames.Length; i++)
+                {
+                    var frame = stackFrames[i];
+                    var method = frame.GetMethod();
+
+                    // Always show last stackFrame
+                    if (!ShowInStackTrace(method) && i < stackFrames.Length - 1)
+                    {
+                        continue;
+                    }
+
+                    var stackFrame = new StackFrameInfo
+                    {
+                        StackFrame = frame,
+                        FilePath = frame.GetFileName(),
+                        LineNumber = frame.GetFileLineNumber(),
+                        MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()),
+                    };
+
+                    if (string.IsNullOrEmpty(stackFrame.FilePath))
+                    {
+                        // .NET Framework and older versions of mono don't support portable PDBs
+                        // so we read it manually to get file name and line information
+                        portablePdbReader.PopulateStackFrame(stackFrame, method, frame.GetILOffset());
+                    }
+
+                    frames.Add(stackFrame);
+                }
+
+                return frames;
+            }
+        }
+
+        internal static MethodDisplayInfo GetMethodDisplayString(MethodBase method)
+        {
+            // Special case: no method available
+            if (method == null)
+            {
+                return null;
+            }
+
+            var methodDisplayInfo = new MethodDisplayInfo();
+
+            // Type name
+            var type = method.DeclaringType;
+
+            var methodName = method.Name;
+
+            if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) &&
+                (typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type)))
+            {
+                // Convert StateMachine methods to correct overload +MoveNext()
+                if (TryResolveStateMachineMethod(ref method, out type))
+                {
+                    methodDisplayInfo.SubMethod = methodName;
+                }
+            }
+            // ResolveStateMachineMethod may have set declaringType to null
+            if (type != null)
+            {
+                methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true);
+            }
+
+            // Method name
+            methodDisplayInfo.Name = method.Name;
+            if (method.IsGenericMethod)
+            {
+                var genericArguments = string.Join(", ", method.GetGenericArguments()
+                    .Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
+                methodDisplayInfo.GenericArguments += "<" + genericArguments + ">";
+            }
+
+            // Method parameters
+            methodDisplayInfo.Parameters = method.GetParameters().Select(parameter =>
+            {
+                var parameterType = parameter.ParameterType;
+
+                var prefix = string.Empty;
+                if (parameter.IsOut)
+                {
+                    prefix = "out";
+                }
+                else if (parameterType != null && parameterType.IsByRef)
+                {
+                    prefix = "ref";
+                }
+
+                var parameterTypeString = "?";
+                if (parameterType != null)
+                {
+                    if (parameterType.IsByRef)
+                    {
+                        parameterType = parameterType.GetElementType();
+                    }
+
+                    parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true);
+                }
+
+                return new ParameterDisplayInfo
+                {
+                    Prefix = prefix,
+                    Name = parameter.Name,
+                    Type = parameterTypeString,
+                };
+            });
+
+            return methodDisplayInfo;
+        }
+
+        private static bool ShowInStackTrace(MethodBase method)
+        {
+            Debug.Assert(method != null);
+
+            // Don't show any methods marked with the StackTraceHiddenAttribute
+            // https://github.com/dotnet/coreclr/pull/14652
+            if (HasStackTraceHiddenAttribute(method))
+            {
+                return false;
+            }
+
+
+            var type = method.DeclaringType;
+            if (type == null)
+            {
+                return true;
+            }
+
+            if (HasStackTraceHiddenAttribute(type))
+            {
+                return false;
+            }
+
+            // Fallbacks for runtime pre-StackTraceHiddenAttribute
+            if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
+            {
+                return false;
+            }
+            else if (type == typeof(TaskAwaiter) ||
+                type == typeof(TaskAwaiter<>) ||
+                type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
+                type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
+            {
+                switch (method.Name)
+                {
+                    case "HandleNonSuccessAndDebuggerNotification":
+                    case "ThrowForNonSuccess":
+                    case "ValidateEnd":
+                    case "GetResult":
+                        return false;
+                }
+            }
+
+            return true;
+        }
+
+        private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
+        {
+            Debug.Assert(method != null);
+            Debug.Assert(method.DeclaringType != null);
+
+            declaringType = method.DeclaringType;
+
+            var parentType = declaringType.DeclaringType;
+            if (parentType == null)
+            {
+                return false;
+            }
+
+            var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
+            if (methods == null)
+            {
+                return false;
+            }
+
+            foreach (var candidateMethod in methods)
+            {
+                var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
+                if (attributes == null)
+                {
+                    continue;
+                }
+
+                foreach (var asma in attributes)
+                {
+                    if (asma.StateMachineType == declaringType)
+                    {
+                        method = candidateMethod;
+                        declaringType = candidateMethod.DeclaringType;
+                        // Mark the iterator as changed; so it gets the + annotation of the original method
+                        // async statemachines resolve directly to their builder methods so aren't marked as changed
+                        return asma is IteratorStateMachineAttribute;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private static bool HasStackTraceHiddenAttribute(MemberInfo memberInfo)
+        {
+            IList<CustomAttributeData> attributes;
+            try
+            {
+                // Accessing MembmerInfo.GetCustomAttributesData throws for some types (such as types in dynamically generated assemblies).
+                // We'll skip looking up StackTraceHiddenAttributes on such types.
+                attributes = memberInfo.GetCustomAttributesData();
+            }
+            catch
+            {
+                return false;
+            }
+
+            for (var i = 0; i < attributes.Count; i++)
+            {
+                if (attributes[i].AttributeType.Name == "StackTraceHiddenAttribute")
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+}

+ 38 - 0
src/Shared/WebEncoders/Properties/EncoderResources.cs

@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Globalization;
+
+namespace Microsoft.Extensions.WebEncoders.Sources
+{
+    // TODO using a resx file. project.json, unfortunately, fails to embed resx files when there are also compile items
+    // in the contentFiles section. Revisit once we convert repos to MSBuild
+    internal static class EncoderResources
+    {
+        /// <summary>
+        /// Invalid {0}, {1} or {2} length.
+        /// </summary>
+        internal static readonly string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length.";
+
+        /// <summary>
+        /// Malformed input: {0} is an invalid input length.
+        /// </summary>
+        internal static readonly string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length.";
+
+        /// <summary>
+        /// Invalid {0}, {1} or {2} length.
+        /// </summary>
+        internal static string FormatWebEncoders_InvalidCountOffsetOrLength(object p0, object p1, object p2)
+        {
+            return string.Format(CultureInfo.CurrentCulture, WebEncoders_InvalidCountOffsetOrLength, p0, p1, p2);
+        }
+
+        /// <summary>
+        /// Malformed input: {0} is an invalid input length.
+        /// </summary>
+        internal static string FormatWebEncoders_MalformedInput(object p0)
+        {
+            return string.Format(CultureInfo.CurrentCulture, WebEncoders_MalformedInput, p0);
+        }
+    }
+}

+ 388 - 0
src/Shared/WebEncoders/WebEncoders.cs

@@ -0,0 +1,388 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using Microsoft.Extensions.WebEncoders.Sources;
+
+#if WebEncoders_In_WebUtilities
+namespace Microsoft.AspNetCore.WebUtilities
+#else
+namespace Microsoft.Extensions.Internal
+#endif
+{
+    /// <summary>
+    /// Contains utility APIs to assist with common encoding and decoding operations.
+    /// </summary>
+#if WebEncoders_In_WebUtilities
+    public
+#else
+    internal
+#endif
+    static class WebEncoders
+    {
+        private static readonly byte[] EmptyBytes = new byte[0];
+
+        /// <summary>
+        /// Decodes a base64url-encoded string.
+        /// </summary>
+        /// <param name="input">The base64url-encoded input to decode.</param>
+        /// <returns>The base64url-decoded form of the input.</returns>
+        /// <remarks>
+        /// The input must not contain any whitespace or padding characters.
+        /// Throws <see cref="FormatException"/> if the input is malformed.
+        /// </remarks>
+        public static byte[] Base64UrlDecode(string input)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+
+            return Base64UrlDecode(input, offset: 0, count: input.Length);
+        }
+
+        /// <summary>
+        /// Decodes a base64url-encoded substring of a given string.
+        /// </summary>
+        /// <param name="input">A string containing the base64url-encoded input to decode.</param>
+        /// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
+        /// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
+        /// <returns>The base64url-decoded form of the input.</returns>
+        /// <remarks>
+        /// The input must not contain any whitespace or padding characters.
+        /// Throws <see cref="FormatException"/> if the input is malformed.
+        /// </remarks>
+        public static byte[] Base64UrlDecode(string input, int offset, int count)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+
+            ValidateParameters(input.Length, nameof(input), offset, count);
+
+            // Special-case empty input
+            if (count == 0)
+            {
+                return EmptyBytes;
+            }
+
+            // Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form.
+            var buffer = new char[GetArraySizeRequiredToDecode(count)];
+
+            return Base64UrlDecode(input, offset, buffer, bufferOffset: 0, count: count);
+        }
+
+        /// <summary>
+        /// Decodes a base64url-encoded <paramref name="input"/> into a <c>byte[]</c>.
+        /// </summary>
+        /// <param name="input">A string containing the base64url-encoded input to decode.</param>
+        /// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
+        /// <param name="buffer">
+        /// Scratch buffer to hold the <see cref="char"/>s to decode. Array must be large enough to hold
+        /// <paramref name="bufferOffset"/> and <paramref name="count"/> characters as well as Base64 padding
+        /// characters. Content is not preserved.
+        /// </param>
+        /// <param name="bufferOffset">
+        /// The offset into <paramref name="buffer"/> at which to begin writing the <see cref="char"/>s to decode.
+        /// </param>
+        /// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
+        /// <returns>The base64url-decoded form of the <paramref name="input"/>.</returns>
+        /// <remarks>
+        /// The input must not contain any whitespace or padding characters.
+        /// Throws <see cref="FormatException"/> if the input is malformed.
+        /// </remarks>
+        public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, int bufferOffset, int count)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer));
+            }
+
+            ValidateParameters(input.Length, nameof(input), offset, count);
+            if (bufferOffset < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(bufferOffset));
+            }
+
+            if (count == 0)
+            {
+                return EmptyBytes;
+            }
+
+            // Assumption: input is base64url encoded without padding and contains no whitespace.
+
+            var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
+            var arraySizeRequired = checked(count + paddingCharsToAdd);
+            Debug.Assert(arraySizeRequired % 4 == 0, "Invariant: Array length must be a multiple of 4.");
+
+            if (buffer.Length - bufferOffset < arraySizeRequired)
+            {
+                throw new ArgumentException(
+                    string.Format(
+                        CultureInfo.CurrentCulture,
+                        EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
+                        nameof(count),
+                        nameof(bufferOffset),
+                        nameof(input)),
+                    nameof(count));
+            }
+
+            // Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'.
+            var i = bufferOffset;
+            for (var j = offset; i - bufferOffset < count; i++, j++)
+            {
+                var ch = input[j];
+                if (ch == '-')
+                {
+                    buffer[i] = '+';
+                }
+                else if (ch == '_')
+                {
+                    buffer[i] = '/';
+                }
+                else
+                {
+                    buffer[i] = ch;
+                }
+            }
+
+            // Add the padding characters back.
+            for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--)
+            {
+                buffer[i] = '=';
+            }
+
+            // Decode.
+            // If the caller provided invalid base64 chars, they'll be caught here.
+            return Convert.FromBase64CharArray(buffer, bufferOffset, arraySizeRequired);
+        }
+
+        /// <summary>
+        /// Gets the minimum <c>char[]</c> size required for decoding of <paramref name="count"/> characters
+        /// with the <see cref="Base64UrlDecode(string, int, char[], int, int)"/> method.
+        /// </summary>
+        /// <param name="count">The number of characters to decode.</param>
+        /// <returns>
+        /// The minimum <c>char[]</c> size required for decoding  of <paramref name="count"/> characters.
+        /// </returns>
+        public static int GetArraySizeRequiredToDecode(int count)
+        {
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count));
+            }
+
+            if (count == 0)
+            {
+                return 0;
+            }
+
+            var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
+
+            return checked(count + numPaddingCharsToAdd);
+        }
+
+        /// <summary>
+        /// Encodes <paramref name="input"/> using base64url encoding.
+        /// </summary>
+        /// <param name="input">The binary input to encode.</param>
+        /// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
+        public static string Base64UrlEncode(byte[] input)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+
+            return Base64UrlEncode(input, offset: 0, count: input.Length);
+        }
+
+        /// <summary>
+        /// Encodes <paramref name="input"/> using base64url encoding.
+        /// </summary>
+        /// <param name="input">The binary input to encode.</param>
+        /// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
+        /// <param name="count">The number of bytes from <paramref name="input"/> to encode.</param>
+        /// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
+        public static string Base64UrlEncode(byte[] input, int offset, int count)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+
+            ValidateParameters(input.Length, nameof(input), offset, count);
+
+            // Special-case empty input
+            if (count == 0)
+            {
+                return string.Empty;
+            }
+
+            var buffer = new char[GetArraySizeRequiredToEncode(count)];
+            var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count);
+
+            return new String(buffer, startIndex: 0, length: numBase64Chars);
+        }
+
+        /// <summary>
+        /// Encodes <paramref name="input"/> using base64url encoding.
+        /// </summary>
+        /// <param name="input">The binary input to encode.</param>
+        /// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
+        /// <param name="output">
+        /// Buffer to receive the base64url-encoded form of <paramref name="input"/>. Array must be large enough to
+        /// hold <paramref name="outputOffset"/> characters and the full base64-encoded form of
+        /// <paramref name="input"/>, including padding characters.
+        /// </param>
+        /// <param name="outputOffset">
+        /// The offset into <paramref name="output"/> at which to begin writing the base64url-encoded form of
+        /// <paramref name="input"/>.
+        /// </param>
+        /// <param name="count">The number of <c>byte</c>s from <paramref name="input"/> to encode.</param>
+        /// <returns>
+        /// The number of characters written to <paramref name="output"/>, less any padding characters.
+        /// </returns>
+        public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count)
+        {
+            if (input == null)
+            {
+                throw new ArgumentNullException(nameof(input));
+            }
+            if (output == null)
+            {
+                throw new ArgumentNullException(nameof(output));
+            }
+
+            ValidateParameters(input.Length, nameof(input), offset, count);
+            if (outputOffset < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(outputOffset));
+            }
+
+            var arraySizeRequired = GetArraySizeRequiredToEncode(count);
+            if (output.Length - outputOffset < arraySizeRequired)
+            {
+                throw new ArgumentException(
+                    string.Format(
+                        CultureInfo.CurrentCulture,
+                        EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
+                        nameof(count),
+                        nameof(outputOffset),
+                        nameof(output)),
+                    nameof(count));
+            }
+
+            // Special-case empty input.
+            if (count == 0)
+            {
+                return 0;
+            }
+
+            // Use base64url encoding with no padding characters. See RFC 4648, Sec. 5.
+
+            // Start with default Base64 encoding.
+            var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset);
+
+            // Fix up '+' -> '-' and '/' -> '_'. Drop padding characters.
+            for (var i = outputOffset; i - outputOffset < numBase64Chars; i++)
+            {
+                var ch = output[i];
+                if (ch == '+')
+                {
+                    output[i] = '-';
+                }
+                else if (ch == '/')
+                {
+                    output[i] = '_';
+                }
+                else if (ch == '=')
+                {
+                    // We've reached a padding character; truncate the remainder.
+                    return i - outputOffset;
+                }
+            }
+
+            return numBase64Chars;
+        }
+
+        /// <summary>
+        /// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
+        /// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
+        /// </summary>
+        /// <param name="count">The number of characters to encode.</param>
+        /// <returns>
+        /// The minimum output <c>char[]</c> size required for encoding <paramref name="count"/> <see cref="byte"/>s.
+        /// </returns>
+        public static int GetArraySizeRequiredToEncode(int count)
+        {
+            var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
+            return checked(numWholeOrPartialInputBlocks * 4);
+        }
+
+        private static int GetNumBase64PaddingCharsInString(string str)
+        {
+            // Assumption: input contains a well-formed base64 string with no whitespace.
+
+            // base64 guaranteed have 0 - 2 padding characters.
+            if (str[str.Length - 1] == '=')
+            {
+                if (str[str.Length - 2] == '=')
+                {
+                    return 2;
+                }
+                return 1;
+            }
+            return 0;
+        }
+
+        private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
+        {
+            switch (inputLength % 4)
+            {
+                case 0:
+                    return 0;
+                case 2:
+                    return 2;
+                case 3:
+                    return 1;
+                default:
+                    throw new FormatException(
+                        string.Format(
+                            CultureInfo.CurrentCulture,
+                            EncoderResources.WebEncoders_MalformedInput,
+                            inputLength));
+            }
+        }
+
+        private static void ValidateParameters(int bufferLength, string inputName, int offset, int count)
+        {
+            if (offset < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(offset));
+            }
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count));
+            }
+            if (bufferLength - offset < count)
+            {
+                throw new ArgumentException(
+                    string.Format(
+                        CultureInfo.CurrentCulture,
+                        EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
+                        nameof(count),
+                        nameof(offset),
+                        inputName),
+                    nameof(count));
+            }
+        }
+    }
+}

+ 360 - 0
src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs

@@ -0,0 +1,360 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class ClosedGenericMatcherTest
+    {
+        // queryType, interfaceType, expectedResult
+        public static TheoryData<Type, Type, Type> ExtractGenericInterfaceDataSet
+        {
+            get
+            {
+                return new TheoryData<Type, Type, Type>
+                {
+                    // Closed generic types that match given open generic type.
+                    {
+                        typeof(IEnumerable<BaseClass>),
+                        typeof(IEnumerable<>),
+                        typeof(IEnumerable<BaseClass>)
+                    },
+                    {
+                        typeof(IReadOnlyList<int>),
+                        typeof(IReadOnlyList<>),
+                        typeof(IReadOnlyList<int>)
+                    },
+                    {
+                        typeof(KeyValuePair<string, object>),
+                        typeof(KeyValuePair<,>),
+                        typeof(KeyValuePair<string, object>)
+                    },
+                    // Closed generic interfaces that implement sub-interface of given open generic type.
+                    {
+                        typeof(ICollection<BaseClass>),
+                        typeof(IEnumerable<>),
+                        typeof(IEnumerable<BaseClass>)
+                    },
+                    {
+                        typeof(IReadOnlyList<int>),
+                        typeof(IEnumerable<>),
+                        typeof(IEnumerable<int>)
+                    },
+                    {
+                        typeof(IDictionary<string, object>),
+                        typeof(IEnumerable<>),
+                        typeof(IEnumerable<KeyValuePair<string, object>>)
+                    },
+                    // Class that implements closed generic based on given open generic interface.
+                    {
+                        typeof(BaseClass),
+                        typeof(IDictionary<,>),
+                        typeof(IDictionary<string, object>)
+                    },
+                    {
+                        typeof(BaseClass),
+                        typeof(IEquatable<>),
+                        typeof(IEquatable<BaseClass>)
+                    },
+                    {
+                        typeof(BaseClass),
+                        typeof(ICollection<>),
+                        typeof(ICollection<KeyValuePair<string, object>>)
+                    },
+                    // Derived class that implements closed generic based on given open generic interface.
+                    {
+                        typeof(DerivedClass),
+                        typeof(IDictionary<,>),
+                        typeof(IDictionary<string, object>)
+                    },
+                    {
+                        typeof(DerivedClass),
+                        typeof(IEquatable<>),
+                        typeof(IEquatable<BaseClass>)
+                    },
+                    {
+                        typeof(DerivedClass),
+                        typeof(ICollection<>),
+                        typeof(ICollection<KeyValuePair<string, object>>)
+                    },
+                    // Derived class that also implements another interface.
+                    {
+                        typeof(DerivedClassWithComparable),
+                        typeof(IDictionary<,>),
+                        typeof(IDictionary<string, object>)
+                    },
+                    {
+                        typeof(DerivedClassWithComparable),
+                        typeof(IEquatable<>),
+                        typeof(IEquatable<BaseClass>)
+                    },
+                    {
+                        typeof(DerivedClassWithComparable),
+                        typeof(ICollection<>),
+                        typeof(ICollection<KeyValuePair<string, object>>)
+                    },
+                    {
+                        typeof(DerivedClassWithComparable),
+                        typeof(IComparable<>),
+                        typeof(IComparable<DerivedClassWithComparable>)
+                    },
+                    // Derived class using system implementation.
+                    {
+                        typeof(DerivedClassFromSystemImplementation),
+                        typeof(ICollection<>),
+                        typeof(ICollection<BaseClass>)
+                    },
+                    {
+                        typeof(DerivedClassFromSystemImplementation),
+                        typeof(IReadOnlyList<>),
+                        typeof(IReadOnlyList<BaseClass>)
+                    },
+                    {
+                        typeof(DerivedClassFromSystemImplementation),
+                        typeof(IEnumerable<>),
+                        typeof(IEnumerable<BaseClass>)
+                    },
+                    // Not given an open generic type.
+                    {
+                        typeof(IEnumerable<BaseClass>),
+                        typeof(IEnumerable<BaseClass>),
+                        null
+                    },
+                    {
+                        typeof(IEnumerable<BaseClass>),
+                        typeof(IEnumerable),
+                        null
+                    },
+                    {
+                        typeof(IReadOnlyList<int>),
+                        typeof(BaseClass),
+                        null
+                    },
+                    {
+                        typeof(KeyValuePair<,>),
+                        typeof(KeyValuePair<string, object>),
+                        null
+                    },
+                    // Not a match.
+                    {
+                        typeof(IEnumerable<BaseClass>),
+                        typeof(IReadOnlyList<>),
+                        null
+                    },
+                    {
+                        typeof(IList<int>),
+                        typeof(IReadOnlyList<>),
+                        null
+                    },
+                    {
+                        typeof(IDictionary<string, object>),
+                        typeof(KeyValuePair<,>),
+                        null
+                    },
+                };
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(ExtractGenericInterfaceDataSet))]
+        public void ExtractGenericInterface_ReturnsExpectedType(
+            Type queryType,
+            Type interfaceType,
+            Type expectedResult)
+        {
+            // Arrange & Act
+            var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType);
+
+            // Assert
+            Assert.Equal(expectedResult, result);
+        }
+
+        // IEnumerable<int> is preferred because it is defined on the more-derived type.
+        [Fact]
+        public void ExtractGenericInterface_MultipleDefinitionsInherited()
+        {
+            // Arrange
+            var type = typeof(TwoIEnumerableImplementationsInherited);
+
+            // Act
+            var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
+
+            // Sort
+            Assert.Equal(typeof(IEnumerable<int>), result);
+        }
+
+        // IEnumerable<int> is preferred because we sort by Ordinal on the full name.
+        [Fact]
+        public void ExtractGenericInterface_MultipleDefinitionsOnSameType()
+        {
+            // Arrange
+            var type = typeof(TwoIEnumerableImplementationsOnSameClass);
+
+            // Act
+            var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
+
+            // Sort
+            Assert.Equal(typeof(IEnumerable<int>), result); 
+        }
+
+        private class TwoIEnumerableImplementationsOnSameClass : IEnumerable<string>, IEnumerable<int>
+        {
+            IEnumerator<int> IEnumerable<int>.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator<string> IEnumerable<string>.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        private class TwoIEnumerableImplementationsInherited : List<int>, IEnumerable<string>
+        {
+            IEnumerator<string> IEnumerable<string>.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        private class BaseClass : IDictionary<string, object>, IEquatable<BaseClass>
+        {
+            object IDictionary<string, object>.this[string key]
+            {
+                get
+                {
+                    throw new NotImplementedException();
+                }
+
+                set
+                {
+                    throw new NotImplementedException();
+                }
+            }
+
+            int ICollection<KeyValuePair<string, object>>.Count
+            {
+                get
+                {
+                    throw new NotImplementedException();
+                }
+            }
+
+            bool ICollection<KeyValuePair<string, object>>.IsReadOnly
+            {
+                get
+                {
+                    throw new NotImplementedException();
+                }
+            }
+
+            ICollection<string> IDictionary<string, object>.Keys
+            {
+                get
+                {
+                    throw new NotImplementedException();
+                }
+            }
+
+            ICollection<object> IDictionary<string, object>.Values
+            {
+                get
+                {
+                    throw new NotImplementedException();
+                }
+            }
+
+            public bool Equals(BaseClass other)
+            {
+                throw new NotImplementedException();
+            }
+
+            void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
+            {
+                throw new NotImplementedException();
+            }
+
+            void IDictionary<string, object>.Add(string key, object value)
+            {
+                throw new NotImplementedException();
+            }
+
+            void ICollection<KeyValuePair<string, object>>.Clear()
+            {
+                throw new NotImplementedException();
+            }
+
+            bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool IDictionary<string, object>.ContainsKey(string key)
+            {
+                throw new NotImplementedException();
+            }
+
+            void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+
+            bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool IDictionary<string, object>.Remove(string key)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool IDictionary<string, object>.TryGetValue(string key, out object value)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        private class DerivedClass : BaseClass
+        {
+        }
+
+        private class DerivedClassWithComparable : DerivedClass, IComparable<DerivedClassWithComparable>
+        {
+            public int CompareTo(DerivedClassWithComparable other)
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        private class DerivedClassFromSystemImplementation : Collection<BaseClass>
+        {
+        }
+    }
+}

+ 91 - 0
src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs

@@ -0,0 +1,91 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class CopyOnWriteDictionaryHolderTest
+    {
+        [Fact]
+        public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed()
+        {
+            // Arrange
+            var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
+            {
+                { "test-key", "test-value" },
+                { "key2", "key2-value" }
+            };
+
+            var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
+
+            // Act and Assert
+            Assert.Equal("key2-value", holder["key2"]);
+            Assert.Equal(2, holder.Count);
+            Assert.Equal(new string[] { "test-key", "key2" }, holder.Keys.ToArray());
+            Assert.Equal(new object[] { "test-value", "key2-value" }, holder.Values.ToArray());
+            Assert.True(holder.ContainsKey("test-key"));
+
+            object value;
+            Assert.False(holder.TryGetValue("different-key", out value));
+
+            Assert.False(holder.HasBeenCopied);
+            Assert.Same(source, holder.ReadDictionary);
+        }
+
+        [Fact]
+        public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged()
+        {
+            // Arrange
+            var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
+            {
+                { "key1", "value1" },
+                { "key2", "value2" }
+            };
+
+            var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
+
+            // Act
+            holder["key2"] = "value3";
+
+            // Assert
+            Assert.Equal("value2", source["key2"]);
+            Assert.Equal(2, holder.Count);
+            Assert.Equal("value1", holder["key1"]);
+            Assert.Equal("value3", holder["key2"]);
+
+            Assert.True(holder.HasBeenCopied);
+            Assert.NotSame(source, holder.ReadDictionary);
+        }
+
+        [Fact]
+        public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceValueIsAdded()
+        {
+            // Arrange
+            var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
+            {
+                { "key1", "value1" },
+                { "key2", "value2" }
+            };
+
+            var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
+
+            // Act
+            holder.Add("key3", "value3");
+            holder.Remove("key1");
+
+            // Assert
+            Assert.Equal(2, source.Count);
+            Assert.Equal("value1", source["key1"]);
+            Assert.Equal(2, holder.Count);
+            Assert.Equal("value2", holder["KeY2"]);
+            Assert.Equal("value3", holder["key3"]);
+
+            Assert.True(holder.HasBeenCopied);
+            Assert.NotSame(source, holder.ReadDictionary);
+        }
+    }
+}

+ 109 - 0
src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs

@@ -0,0 +1,109 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Moq;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class CopyOnWriteDictionaryTest
+    {
+        [Fact]
+        public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed()
+        {
+            // Arrange
+            var values = new List<object>();
+            var enumerator = Mock.Of<IEnumerator<KeyValuePair<string, object>>>();
+            var sourceDictionary = new Mock<IDictionary<string, object>>(MockBehavior.Strict);
+            sourceDictionary
+                .SetupGet(d => d.Count)
+                .Returns(100)
+                .Verifiable();
+            sourceDictionary
+                .SetupGet(d => d.Values)
+                .Returns(values)
+                .Verifiable();
+            sourceDictionary
+                .Setup(d => d.ContainsKey("test-key"))
+                .Returns(value: true)
+                .Verifiable();
+            sourceDictionary
+                .Setup(d => d.GetEnumerator())
+                .Returns(enumerator)
+                .Verifiable();
+            sourceDictionary
+                .Setup(d => d["key2"])
+                .Returns("key2-value")
+                .Verifiable();
+            object value;
+            sourceDictionary.Setup(d => d.TryGetValue("different-key", out value))
+                            .Returns(false)
+                            .Verifiable();
+
+            var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(sourceDictionary.Object,
+                                                                                  StringComparer.OrdinalIgnoreCase);
+
+            // Act and Assert
+            Assert.Equal("key2-value", copyOnWriteDictionary["key2"]);
+            Assert.Equal(100, copyOnWriteDictionary.Count);
+            Assert.Same(values, copyOnWriteDictionary.Values);
+            Assert.True(copyOnWriteDictionary.ContainsKey("test-key"));
+            Assert.Same(enumerator, copyOnWriteDictionary.GetEnumerator());
+            Assert.False(copyOnWriteDictionary.TryGetValue("different-key", out value));
+            sourceDictionary.Verify();
+        }
+
+        [Fact]
+        public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged()
+        {
+            // Arrange
+            var values = new List<object>();
+            var sourceDictionary = new Dictionary<string, object>
+            {
+                { "key1", "value1" },
+                { "key2", "value2" }
+            };
+            var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(
+                sourceDictionary,
+                StringComparer.OrdinalIgnoreCase);
+
+            // Act
+            copyOnWriteDictionary["key2"] = "value3";
+
+            // Assert
+            Assert.Equal("value2", sourceDictionary["key2"]);
+            Assert.Equal(2, copyOnWriteDictionary.Count);
+            Assert.Equal("value1", copyOnWriteDictionary["key1"]);
+            Assert.Equal("value3", copyOnWriteDictionary["key2"]);
+        }
+
+        [Fact]
+        public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceDictionaryIsModified()
+        {
+            // Arrange
+            var values = new List<object>();
+            var sourceDictionary = new Dictionary<string, object>
+            {
+                { "key1", "value1" },
+                { "key2", "value2" }
+            };
+            var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(
+                sourceDictionary,
+                StringComparer.OrdinalIgnoreCase);
+
+            // Act
+            copyOnWriteDictionary.Add("key3", "value3");
+            copyOnWriteDictionary.Remove("key1");
+
+
+            // Assert
+            Assert.Equal(2, sourceDictionary.Count);
+            Assert.Equal("value1", sourceDictionary["key1"]);
+            Assert.Equal(2, copyOnWriteDictionary.Count);
+            Assert.Equal("value2", copyOnWriteDictionary["KeY2"]);
+            Assert.Equal("value3", copyOnWriteDictionary["key3"]);
+        }
+    }
+}

+ 29 - 0
src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj

@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
+    <DebugType>portable</DebugType>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
+    <Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
+    <Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)StackTrace\StackFrame\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="FSharp.Core" />
+    <Reference Include="System.Reflection.Metadata" />
+    <Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\testassets\ThrowingLibrary\ThrowingLibrary.csproj" />
+  </ItemGroup>
+
+</Project>

+ 634 - 0
src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs

@@ -0,0 +1,634 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.FSharp.Control;
+using Microsoft.FSharp.Core;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class ObjectMethodExecutorTest
+    {
+        private TestObject _targetObject = new TestObject();
+        private TypeInfo targetTypeInfo = typeof(TestObject).GetTypeInfo();
+
+        [Fact]
+        public void ExecuteValueMethod()
+        {
+            var executor = GetExecutorForMethod("ValueMethod");
+            var result = executor.Execute(
+                _targetObject,
+                new object[] { 10, 20 });
+            Assert.False(executor.IsMethodAsync);
+            Assert.Equal(30, (int)result);
+        }
+
+        [Fact]
+        public void ExecuteVoidValueMethod()
+        {
+            var executor = GetExecutorForMethod("VoidValueMethod");
+            var result = executor.Execute(
+                _targetObject,
+                new object[] { 10 });
+            Assert.False(executor.IsMethodAsync);
+            Assert.Null(result);
+        }
+
+        [Fact]
+        public void ExecuteValueMethodWithReturnType()
+        {
+            var executor = GetExecutorForMethod("ValueMethodWithReturnType");
+            var result = executor.Execute(
+                _targetObject,
+                new object[] { 10 });
+            var resultObject = Assert.IsType<TestObject>(result);
+            Assert.False(executor.IsMethodAsync);
+            Assert.Equal("Hello", resultObject.value);
+        }
+
+        [Fact]
+        public void ExecuteValueMethodUpdateValue()
+        {
+            var executor = GetExecutorForMethod("ValueMethodUpdateValue");
+            var parameter = new TestObject();
+            var result = executor.Execute(
+                _targetObject,
+                new object[] { parameter });
+            var resultObject = Assert.IsType<TestObject>(result);
+            Assert.False(executor.IsMethodAsync);
+            Assert.Equal("HelloWorld", resultObject.value);
+        }
+
+        [Fact]
+        public void ExecuteValueMethodWithReturnTypeThrowsException()
+        {
+            var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsException");
+            var parameter = new TestObject();
+            Assert.False(executor.IsMethodAsync);
+            Assert.Throws<NotImplementedException>(
+                        () => executor.Execute(
+                            _targetObject,
+                            new object[] { parameter }));
+        }
+
+        [Fact]
+        public async Task ExecuteValueMethodAsync()
+        {
+            var executor = GetExecutorForMethod("ValueMethodAsync");
+            var result = await executor.ExecuteAsync(
+                _targetObject,
+                new object[] { 10, 20 });
+            Assert.True(executor.IsMethodAsync);
+            Assert.Equal(30, (int)result);
+        }
+
+        [Fact]
+        public async Task ExecuteValueMethodWithReturnTypeAsync()
+        {
+            var executor = GetExecutorForMethod("ValueMethodWithReturnTypeAsync");
+            var result = await executor.ExecuteAsync(
+                _targetObject,
+                new object[] { 10 });
+            var resultObject = Assert.IsType<TestObject>(result);
+            Assert.True(executor.IsMethodAsync);
+            Assert.Equal("Hello", resultObject.value);
+        }
+
+        [Fact]
+        public async Task ExecuteValueMethodUpdateValueAsync()
+        {
+            var executor = GetExecutorForMethod("ValueMethodUpdateValueAsync");
+            var parameter = new TestObject();
+            var result = await executor.ExecuteAsync(
+                _targetObject,
+                new object[] { parameter });
+            var resultObject = Assert.IsType<TestObject>(result);
+            Assert.True(executor.IsMethodAsync);
+            Assert.Equal("HelloWorld", resultObject.value);
+        }
+
+        [Fact]
+        public async Task ExecuteValueMethodWithReturnTypeThrowsExceptionAsync()
+        {
+            var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsExceptionAsync");
+            var parameter = new TestObject();
+            Assert.True(executor.IsMethodAsync);
+            await Assert.ThrowsAsync<NotImplementedException>(
+                    async () => await executor.ExecuteAsync(
+                            _targetObject,
+                            new object[] { parameter }));
+        }
+
+        [Fact]
+        public async Task ExecuteValueMethodWithReturnVoidThrowsExceptionAsync()
+        {
+            var executor = GetExecutorForMethod("ValueMethodWithReturnVoidThrowsExceptionAsync");
+            var parameter = new TestObject();
+            Assert.True(executor.IsMethodAsync);
+            await Assert.ThrowsAsync<NotImplementedException>(
+                    async () => await executor.ExecuteAsync(
+                            _targetObject,
+                            new object[] { parameter }));
+        }
+
+        [Fact]
+        public void GetDefaultValueForParameters_ReturnsSuppliedValues()
+        {
+            var suppliedDefaultValues = new object[] { 123, "test value" };
+            var executor = GetExecutorForMethod("MethodWithMultipleParameters", suppliedDefaultValues);
+            Assert.Equal(suppliedDefaultValues[0], executor.GetDefaultValueForParameter(0));
+            Assert.Equal(suppliedDefaultValues[1], executor.GetDefaultValueForParameter(1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => executor.GetDefaultValueForParameter(2));
+        }
+
+        [Fact]
+        public void GetDefaultValueForParameters_ThrowsIfNoneWereSupplied()
+        {
+            var executor = GetExecutorForMethod("MethodWithMultipleParameters");
+            Assert.Throws<InvalidOperationException>(() => executor.GetDefaultValueForParameter(0));
+        }
+
+        [Fact]
+        public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync");
+
+            // Act
+            var result = await (TestAwaitable<TestObject>)executor.Execute(_targetObject, new object[] { "Hello", 123 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(TestObject), executor.AsyncResultType);
+            Assert.NotNull(result);
+            Assert.Equal("Hello 123", result.value);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync");
+
+            // Act
+            var result = await (TestAwaitable<int>)executor.Execute(_targetObject, new object[] { 123, 456 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(int), executor.AsyncResultType);
+            Assert.Equal(579, result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { "Hello", 123 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(TestObject), executor.AsyncResultType);
+            Assert.NotNull(result);
+            Assert.IsType<TestObject>(result);
+            Assert.Equal("Hello 123", ((TestObject)result).value);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { 123, 456 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(int), executor.AsyncResultType);
+            Assert.NotNull(result);
+            Assert.IsType<int>(result);
+            Assert.Equal(579, (int)result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("VoidValueMethodAsync");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(void), executor.AsyncResultType);
+            Assert.Null(result);
+        }
+        
+        [Fact]
+        public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[0]);
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("Used UnsafeOnCompleted", (string)result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[0]);
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("Used OnCompleted", (string)result);
+        }
+        
+        [Fact]
+        public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("ValueTaskOfValueType");
+
+            // Act
+            var result = await (ValueTask<int>)executor.Execute(_targetObject, new object[] { 123 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(int), executor.AsyncResultType);
+            Assert.Equal(123, result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("ValueTaskOfReferenceType");
+
+            // Act
+            var result = await (ValueTask<string>)executor.Execute(_targetObject, new object[] { "test result" });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("test result", result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("ValueTaskOfValueType");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(int), executor.AsyncResultType);
+            Assert.NotNull(result);
+            Assert.Equal(123, (int)result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("ValueTaskOfReferenceType");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("test result", result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("FSharpAsyncMethod");
+
+            // Act
+            var fsharpAsync = (FSharpAsync<string>)executor.Execute(_targetObject, new object[] { "test result" });
+            var result = await FSharpAsync.StartAsTask(fsharpAsync,
+                FSharpOption<TaskCreationOptions>.None,
+                FSharpOption<CancellationToken>.None);
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("test result", result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("FSharpAsyncFailureMethod");
+
+            // Act
+            var fsharpAsync = (FSharpAsync<string>)executor.Execute(_targetObject, new object[] { "test result" });
+            var resultTask = FSharpAsync.StartAsTask(fsharpAsync,
+                FSharpOption<TaskCreationOptions>.None,
+                FSharpOption<CancellationToken>.None);
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+
+            var exception = await Assert.ThrowsAsync<AggregateException>(async () => await resultTask);
+            Assert.IsType<InvalidOperationException>(exception.InnerException);
+            Assert.Equal("Test exception", exception.InnerException.Message);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("FSharpAsyncMethod");
+
+            // Act
+            var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+            Assert.Equal("test result", result);
+        }
+
+        [Fact]
+        public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync()
+        {
+            // Arrange
+            var executor = GetExecutorForMethod("FSharpAsyncFailureMethod");
+
+            // Act
+            var resultTask = executor.ExecuteAsync(_targetObject, new object[] { "test result" });
+
+            // Assert
+            Assert.True(executor.IsMethodAsync);
+            Assert.Same(typeof(string), executor.AsyncResultType);
+
+            var exception = await Assert.ThrowsAsync<AggregateException>(async () => await resultTask);
+            Assert.IsType<InvalidOperationException>(exception.InnerException);
+            Assert.Equal("Test exception", exception.InnerException.Message);
+        }
+
+        private ObjectMethodExecutor GetExecutorForMethod(string methodName)
+        {
+            var method = typeof(TestObject).GetMethod(methodName);
+            return ObjectMethodExecutor.Create(method, targetTypeInfo);
+        }
+
+        private ObjectMethodExecutor GetExecutorForMethod(string methodName, object[] parameterDefaultValues)
+        {
+            var method = typeof(TestObject).GetMethod(methodName);
+            return ObjectMethodExecutor.Create(method, targetTypeInfo, parameterDefaultValues);
+        }
+
+        public class TestObject
+        {
+            public string value;
+            public int ValueMethod(int i, int j)
+            {
+                return i + j;
+            }
+
+            public void VoidValueMethod(int i)
+            {
+
+            }
+
+            public TestObject ValueMethodWithReturnType(int i)
+            {
+                return new TestObject() { value = "Hello" }; ;
+            }
+
+            public TestObject ValueMethodWithReturnTypeThrowsException(TestObject i)
+            {
+                throw new NotImplementedException("Not Implemented Exception");
+            }
+
+            public TestObject ValueMethodUpdateValue(TestObject parameter)
+            {
+                parameter.value = "HelloWorld";
+                return parameter;
+            }
+
+            public Task<int> ValueMethodAsync(int i, int j)
+            {
+                return Task.FromResult<int>(i + j);
+            }
+
+            public async Task VoidValueMethodAsync(int i)
+            {
+                await ValueMethodAsync(3, 4);
+            }
+            public Task<TestObject> ValueMethodWithReturnTypeAsync(int i)
+            {
+                return Task.FromResult<TestObject>(new TestObject() { value = "Hello" });
+            }
+
+            public async Task ValueMethodWithReturnVoidThrowsExceptionAsync(TestObject i)
+            {
+                await Task.CompletedTask;
+                throw new NotImplementedException("Not Implemented Exception");
+            }
+
+            public async Task<TestObject> ValueMethodWithReturnTypeThrowsExceptionAsync(TestObject i)
+            {
+                await Task.CompletedTask;
+                throw new NotImplementedException("Not Implemented Exception");
+            }
+
+            public Task<TestObject> ValueMethodUpdateValueAsync(TestObject parameter)
+            {
+                parameter.value = "HelloWorld";
+                return Task.FromResult<TestObject>(parameter);
+            }
+
+            public TestAwaitable<TestObject> CustomAwaitableOfReferenceTypeAsync(
+                string input1,
+                int input2)
+            {
+                return new TestAwaitable<TestObject>(new TestObject
+                {
+                    value = $"{input1} {input2}"
+                });
+            }
+
+            public TestAwaitable<int> CustomAwaitableOfValueTypeAsync(
+                int input1,
+                int input2)
+            {
+                return new TestAwaitable<int>(input1 + input2);
+            }
+
+            public TestAwaitableWithICriticalNotifyCompletion CustomAwaitableWithICriticalNotifyCompletion()
+            {
+                return new TestAwaitableWithICriticalNotifyCompletion();
+            }
+
+            public TestAwaitableWithoutICriticalNotifyCompletion CustomAwaitableWithoutICriticalNotifyCompletion()
+            {
+                return new TestAwaitableWithoutICriticalNotifyCompletion();
+            }
+            
+            public ValueTask<int> ValueTaskOfValueType(int result)
+            {
+                return new ValueTask<int>(result);
+            }
+
+            public ValueTask<string> ValueTaskOfReferenceType(string result)
+            {
+                return new ValueTask<string>(result);
+            }
+
+            public void MethodWithMultipleParameters(int valueTypeParam, string referenceTypeParam)
+            {
+            }
+
+            public FSharpAsync<string> FSharpAsyncMethod(string parameter)
+            {
+                return FSharpAsync.AwaitTask(Task.FromResult(parameter));
+            }
+
+            public FSharpAsync<string> FSharpAsyncFailureMethod(string parameter)
+            {
+                return FSharpAsync.AwaitTask(
+                    Task.FromException<string>(new InvalidOperationException("Test exception")));
+            }
+        }
+
+        public class TestAwaitable<T>
+        {
+            private T _result;
+            private bool _isCompleted;
+            private List<Action> _onCompletedCallbacks = new List<Action>();
+
+            public TestAwaitable(T result)
+            {
+                _result = result;
+
+                // Simulate a brief delay before completion
+                ThreadPool.QueueUserWorkItem(_ =>
+                {
+                    Thread.Sleep(100);
+                    SetCompleted();
+                });
+            }
+
+            private void SetCompleted()
+            {
+                _isCompleted = true;
+
+                foreach (var callback in _onCompletedCallbacks)
+                {
+                    callback();
+                }
+            }
+
+            public TestAwaiter GetAwaiter()
+            {
+                return new TestAwaiter(this);
+            }
+
+            public struct TestAwaiter : INotifyCompletion
+            {
+                private TestAwaitable<T> _owner;
+
+                public TestAwaiter(TestAwaitable<T> owner) : this()
+                {
+                    _owner = owner;
+                }
+
+                public bool IsCompleted => _owner._isCompleted;
+
+                public void OnCompleted(Action continuation)
+                {
+                    if (_owner._isCompleted)
+                    {
+                        continuation();
+                    }
+                    else
+                    {
+                        _owner._onCompletedCallbacks.Add(continuation);
+                    }
+                }
+
+                public T GetResult()
+                {
+                    return _owner._result;
+                }
+            }
+        }
+
+        public class TestAwaitableWithICriticalNotifyCompletion
+        {
+            public TestAwaiterWithICriticalNotifyCompletion GetAwaiter()
+                => new TestAwaiterWithICriticalNotifyCompletion();
+        }
+
+        public class TestAwaitableWithoutICriticalNotifyCompletion
+        {
+            public TestAwaiterWithoutICriticalNotifyCompletion GetAwaiter()
+                => new TestAwaiterWithoutICriticalNotifyCompletion();
+        }
+
+        public class TestAwaiterWithICriticalNotifyCompletion
+            : CompletionTrackingAwaiterBase, ICriticalNotifyCompletion
+        {
+        }
+
+        public class TestAwaiterWithoutICriticalNotifyCompletion
+            : CompletionTrackingAwaiterBase, INotifyCompletion
+        {
+        }
+
+        public class CompletionTrackingAwaiterBase
+        {
+            private string _result;
+
+            public bool IsCompleted { get; private set; }
+
+            public string GetResult() => _result;
+
+            public void OnCompleted(Action continuation)
+            {
+                _result = "Used OnCompleted";
+                IsCompleted = true;
+                continuation();
+            }
+
+            public void UnsafeOnCompleted(Action continuation)
+            {
+                _result = "Used UnsafeOnCompleted";
+                IsCompleted = true;
+                continuation();
+            }
+        }
+    }
+}

+ 187 - 0
src/Shared/test/Shared.Tests/PropertyActivatorTest.cs

@@ -0,0 +1,187 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Reflection;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class PropertyActivatorTest
+    {
+        [Fact]
+        public void Activate_InvokesValueAccessorWithExpectedValue()
+        {
+            // Arrange
+            var instance = new TestClass();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var property = typeInfo.GetDeclaredProperty("IntProperty");
+            var invokedWith = -1;
+            var activator = new PropertyActivator<int>(
+                property,
+                valueAccessor: (val) =>
+                {
+                    invokedWith = val;
+                    return val;
+                });
+
+            // Act
+            activator.Activate(instance, 123);
+
+            // Assert
+            Assert.Equal(123, invokedWith);
+        }
+
+        [Fact]
+        public void Activate_SetsPropertyValue()
+        {
+            // Arrange
+            var instance = new TestClass();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var property = typeInfo.GetDeclaredProperty("IntProperty");
+            var activator = new PropertyActivator<int>(property, valueAccessor: (val) => val + 1);
+
+            // Act
+            activator.Activate(instance, 123);
+
+            // Assert
+            Assert.Equal(124, instance.IntProperty);
+        }
+
+        [Fact]
+        public void GetPropertiesToActivate_RestrictsActivatableProperties()
+        {
+            // Arrange
+            var instance = new TestClass();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var expectedPropertyInfo = typeInfo.GetDeclaredProperty("ActivatableProperty");
+
+            // Act
+            var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
+                type: typeof(TestClass),
+                activateAttributeType: typeof(TestActivateAttribute),
+                createActivateInfo:
+                (propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val + 1));
+
+            // Assert
+            Assert.Collection(
+                propertiesToActivate,
+                (activator) =>
+                {
+                    Assert.Equal(expectedPropertyInfo, activator.PropertyInfo);
+                });
+        }
+
+        [Fact]
+        public void GetPropertiesToActivate_CanCreateCustomPropertyActivators()
+        {
+            // Arrange
+            var instance = new TestClass();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var expectedPropertyInfo = typeInfo.GetDeclaredProperty("IntProperty");
+
+            // Act
+            var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
+                type: typeof(TestClass),
+                activateAttributeType: typeof(TestActivateAttribute),
+                createActivateInfo:
+                (propertyInfo) => new PropertyActivator<int>(expectedPropertyInfo, valueAccessor: (val) => val + 1));
+
+            // Assert
+            Assert.Collection(
+                propertiesToActivate,
+                (activator) =>
+                {
+                    Assert.Equal(expectedPropertyInfo, activator.PropertyInfo);
+                });
+        }
+
+        [Fact]
+        public void GetPropertiesToActivate_ExcludesNonPublic()
+        {
+            // Arrange
+            var instance = new TestClassWithPropertyVisiblity();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var expectedPropertyInfo = typeInfo.GetDeclaredProperty("Public");
+
+            // Act
+            var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
+                typeof(TestClassWithPropertyVisiblity),
+                typeof(TestActivateAttribute),
+                (propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val));
+
+            // Assert
+            Assert.Single(propertiesToActivate);
+            Assert.Single(propertiesToActivate, p => p.PropertyInfo == expectedPropertyInfo);
+        }
+
+        [Fact]
+        public void GetPropertiesToActivate_IncludesNonPublic()
+        {
+            // Arrange
+            var instance = new TestClassWithPropertyVisiblity();
+            var typeInfo = instance.GetType().GetTypeInfo();
+
+            // Act
+            var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
+                typeof(TestClassWithPropertyVisiblity),
+                typeof(TestActivateAttribute),
+                (propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val),
+                includeNonPublic: true);
+
+            // Assert
+            Assert.Equal(5, propertiesToActivate.Length);
+        }
+
+        private class TestClass
+        {
+            public int IntProperty { get; set; }
+
+            [TestActivate]
+            public int ActivatableProperty { get; set; }
+
+            [TestActivate]
+            public int NoSetterActivatableProperty { get; }
+
+            [TestActivate]
+            public int this[int something] // Not activatable
+            {
+                get
+                {
+                    return 0;
+                }
+            }
+
+            [TestActivate]
+            public static int StaticActivatablProperty { get; set; }
+        }
+
+        private class TestClassWithPropertyVisiblity
+        {
+            [TestActivate]
+            public int Public { get; set; }
+
+            [TestActivate]
+            protected int Protected { get; set; }
+
+            [TestActivate]
+            internal int Internal { get; set; }
+
+            [TestActivate]
+            protected internal int ProtectedInternal {get; set; }
+
+            [TestActivate]
+            private int Private { get; set; }
+        }
+
+        [AttributeUsage(AttributeTargets.Property)]
+        private class TestActivateAttribute : Attribute
+        {
+        }
+
+        private class ActivationInfo
+        {
+            public string Name { get; set; }
+        }
+    }
+}

+ 831 - 0
src/Shared/test/Shared.Tests/PropertyHelperTest.cs

@@ -0,0 +1,831 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class PropertyHelperTest
+    {
+        [Fact]
+        public void PropertyHelper_ReturnsNameCorrectly()
+        {
+            // Arrange
+            var anonymous = new { foo = "bar" };
+            var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
+
+            // Act
+            var helper = new PropertyHelper(property);
+
+            // Assert
+            Assert.Equal("foo", property.Name);
+            Assert.Equal("foo", helper.Name);
+        }
+
+        [Fact]
+        public void PropertyHelper_ReturnsValueCorrectly()
+        {
+            // Arrange
+            var anonymous = new { bar = "baz" };
+            var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
+
+            // Act
+            var helper = new PropertyHelper(property);
+
+            // Assert
+            Assert.Equal("bar", helper.Name);
+            Assert.Equal("baz", helper.GetValue(anonymous));
+        }
+
+        [Fact]
+        public void PropertyHelper_ReturnsGetterDelegate()
+        {
+            // Arrange
+            var anonymous = new { bar = "baz" };
+            var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
+
+            // Act
+            var helper = new PropertyHelper(property);
+
+            // Assert
+            Assert.NotNull(helper.ValueGetter);
+            Assert.Equal("baz", helper.ValueGetter(anonymous));
+        }
+
+        [Fact]
+        public void SetValue_SetsPropertyValue()
+        {
+            // Arrange
+            var expected = "new value";
+            var instance = new BaseClass { PropA = "old value" };
+            var helper = PropertyHelper.GetProperties(
+                instance.GetType()).First(prop => prop.Name == "PropA");
+
+            // Act
+            helper.SetValue(instance, expected);
+
+            // Assert
+            Assert.Equal(expected, instance.PropA);
+        }
+
+        [Fact]
+        public void PropertyHelper_ReturnsSetterDelegate()
+        {
+            // Arrange
+            var expected = "new value";
+            var instance = new BaseClass { PropA = "old value" };
+            var helper = PropertyHelper.GetProperties(
+                instance.GetType()).First(prop => prop.Name == "PropA");
+
+            // Act and Assert
+            Assert.NotNull(helper.ValueSetter);
+            helper.ValueSetter(instance, expected);
+
+            // Assert
+            Assert.Equal(expected, instance.PropA);
+        }
+
+        [Fact]
+        public void PropertyHelper_ReturnsValueCorrectly_ForValueTypes()
+        {
+            // Arrange
+            var anonymous = new { foo = 32 };
+            var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
+
+            // Act
+            var helper = new PropertyHelper(property);
+
+            // Assert
+            Assert.Equal("foo", helper.Name);
+            Assert.Equal(32, helper.GetValue(anonymous));
+        }
+
+        [Fact]
+        public void PropertyHelper_ReturnsCachedPropertyHelper()
+        {
+            // Arrange
+            var anonymous = new { foo = "bar" };
+
+            // Act
+            var helpers1 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo());
+            var helpers2 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo());
+
+            // Assert
+            Assert.Single(helpers1);
+            Assert.Same(helpers1, helpers2);
+            Assert.Same(helpers1[0], helpers2[0]);
+        }
+
+        [Fact]
+        public void PropertyHelper_DoesNotChangeUnderscores()
+        {
+            // Arrange
+            var anonymous = new { bar_baz2 = "foo" };
+
+            // Act + Assert
+            var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
+            Assert.Equal("bar_baz2", helper.Name);
+        }
+
+        [Fact]
+        public void PropertyHelper_DoesNotFindPrivateProperties()
+        {
+            // Arrange
+            var anonymous = new PrivateProperties();
+
+            // Act + Assert
+            var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
+            Assert.Equal("Prop1", helper.Name);
+        }
+
+        [Fact]
+        public void PropertyHelper_DoesNotFindStaticProperties()
+        {
+            // Arrange
+            var anonymous = new Static();
+
+            // Act + Assert
+            var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
+            Assert.Equal("Prop5", helper.Name);
+        }
+
+        [Fact]
+        public void PropertyHelper_DoesNotFindSetOnlyProperties()
+        {
+            // Arrange
+            var anonymous = new SetOnly();
+
+            // Act + Assert
+            var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
+            Assert.Equal("Prop6", helper.Name);
+        }
+
+        [Theory]
+        [InlineData(typeof(int?))]
+        [InlineData(typeof(DayOfWeek?))]
+        public void PropertyHelper_WorksForNullablePrimitiveAndEnumTypes(Type nullableType)
+        {
+            // Act
+            var properties = PropertyHelper.GetProperties(nullableType);
+
+            // Assert
+            Assert.Empty(properties);
+        }
+
+        [Fact]
+        public void PropertyHelper_UnwrapsNullableTypes()
+        {
+            // Arrange
+            var myType = typeof(MyStruct?);
+
+            // Act
+            var properties = PropertyHelper.GetProperties(myType);
+
+            // Assert
+            var property = Assert.Single(properties);
+            Assert.Equal("Foo", property.Name);
+        }
+
+        [Fact]
+        public void PropertyHelper_WorksForStruct()
+        {
+            // Arrange
+            var anonymous = new MyProperties();
+
+            anonymous.IntProp = 3;
+            anonymous.StringProp = "Five";
+
+            // Act + Assert
+            var helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "IntProp"));
+            var helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "StringProp"));
+            Assert.Equal(3, helper1.GetValue(anonymous));
+            Assert.Equal("Five", helper2.GetValue(anonymous));
+        }
+
+        [Fact]
+        public void PropertyHelper_ForDerivedClass()
+        {
+            // Arrange
+            var derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" };
+
+            // Act
+            var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
+
+            // Assert
+            Assert.NotNull(helpers);
+            Assert.Equal(2, helpers.Length);
+
+            var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
+            var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
+
+            Assert.Equal("propAValue", propAHelper.GetValue(derived));
+            Assert.Equal("propBValue", propBHelper.GetValue(derived));
+        }
+
+        [Fact]
+        public void PropertyHelper_ForDerivedClass_WithNew()
+        {
+            // Arrange
+            var derived = new DerivedClassWithNew { PropA = "propAValue" };
+
+            // Act
+            var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
+
+            // Assert
+            Assert.NotNull(helpers);
+            Assert.Equal(2, helpers.Length);
+
+            var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
+            var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
+
+            Assert.Equal("propAValue", propAHelper.GetValue(derived));
+            Assert.Equal("Newed", propBHelper.GetValue(derived));
+        }
+
+        [Fact]
+        public void PropertyHelper_ForDerived_WithVirtual()
+        {
+            // Arrange
+            var derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" };
+
+            // Act
+            var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
+
+            // Assert
+            Assert.NotNull(helpers);
+            Assert.Equal(2, helpers.Length);
+
+            var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
+            var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
+
+            Assert.Equal("OverridenpropAValue", propAHelper.GetValue(derived));
+            Assert.Equal("propBValue", propBHelper.GetValue(derived));
+        }
+
+        [Fact]
+        public void PropertyHelper_ForInterface_ReturnsExpectedProperties()
+        {
+            // Arrange
+            var expectedNames = new[] { "Count", "IsReadOnly" };
+
+            // Act
+            var helpers = PropertyHelper.GetProperties(typeof(ICollection<string>));
+
+            // Assert
+            Assert.Collection(
+                helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal),
+                helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); },
+                helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); });
+        }
+
+        [Fact]
+        public void PropertyHelper_ForDerivedInterface_ReturnsAllProperties()
+        {
+            // Arrange
+            var expectedNames = new[] { "Count", "IsReadOnly", "Keys", "Values" };
+
+            // Act
+            var helpers = PropertyHelper.GetProperties(typeof(IDictionary<string, string>));
+
+            // Assert
+            Assert.Collection(
+                helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal),
+                helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); },
+                helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); },
+                helper => { Assert.Equal(expectedNames[2], helper.Name, StringComparer.Ordinal); },
+                helper => { Assert.Equal(expectedNames[3], helper.Name, StringComparer.Ordinal); });
+        }
+
+        [Fact]
+        public void GetProperties_ExcludesIndexersAndPropertiesWithoutPublicGetters()
+        {
+            // Arrange
+            var type = typeof(DerivedClassWithNonReadableProperties);
+
+            // Act
+            var result = PropertyHelper.GetProperties(type).ToArray();
+
+            // Assert
+            Assert.Equal(3, result.Length);
+            Assert.Equal("Visible", result[0].Name);
+            Assert.Equal("PropA", result[1].Name);
+            Assert.Equal("PropB", result[2].Name);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_NoHiddenProperty()
+        {
+            // Arrange
+            var type = typeof(string);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type).ToArray();
+
+            // Assert
+            var property = Assert.Single(result);
+            Assert.Equal("Length", property.Name);
+            Assert.Equal(typeof(int), property.Property.PropertyType);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_HiddenProperty()
+        {
+            // Arrange
+            var type = typeof(DerivedHiddenProperty);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type).ToArray();
+
+            // Assert
+            Assert.Equal(2, result.Length);
+            Assert.Equal("Id", result[0].Name);
+            Assert.Equal(typeof(string), result[0].Property.PropertyType);
+            Assert.Equal("Name", result[1].Name);
+            Assert.Equal(typeof(string), result[1].Property.PropertyType);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_HiddenProperty_TwoLevels()
+        {
+            // Arrange
+            var type = typeof(DerivedHiddenProperty2);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type).ToArray();
+
+            // Assert
+            Assert.Equal(2, result.Length);
+            Assert.Equal("Id", result[0].Name);
+            Assert.Equal(typeof(Guid), result[0].Property.PropertyType);
+            Assert.Equal("Name", result[1].Name);
+            Assert.Equal(typeof(string), result[1].Property.PropertyType);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_NoHiddenPropertyWithTypeInfoInput()
+        {
+            // Arrange
+            var type = typeof(string);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
+
+            // Assert
+            var property = Assert.Single(result);
+            Assert.Equal("Length", property.Name);
+            Assert.Equal(typeof(int), property.Property.PropertyType);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_HiddenPropertyWithTypeInfoInput()
+        {
+            // Arrange
+            var type = typeof(DerivedHiddenProperty);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
+
+            // Assert
+            Assert.Equal(2, result.Length);
+            Assert.Equal("Id", result[0].Name);
+            Assert.Equal(typeof(string), result[0].Property.PropertyType);
+            Assert.Equal("Name", result[1].Name);
+            Assert.Equal(typeof(string), result[1].Property.PropertyType);
+        }
+
+        [Fact]
+        public void GetVisibleProperties_HiddenProperty_TwoLevelsWithTypeInfoInput()
+        {
+            // Arrange
+            var type = typeof(DerivedHiddenProperty2);
+
+            // Act
+            var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
+
+            // Assert
+            Assert.Equal(2, result.Length);
+            Assert.Equal("Id", result[0].Name);
+            Assert.Equal(typeof(Guid), result[0].Property.PropertyType);
+            Assert.Equal("Name", result[1].Name);
+            Assert.Equal(typeof(string), result[1].Property.PropertyType);
+        }
+
+        [Fact]
+        public void MakeFastPropertySetter_SetsPropertyValues_ForPublicAndNobPublicProperties()
+        {
+            // Arrange
+            var instance = new BaseClass();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var publicProperty = typeInfo.GetDeclaredProperty("PropA");
+            var protectedProperty = typeInfo.GetDeclaredProperty("PropProtected");
+            var publicPropertySetter = PropertyHelper.MakeFastPropertySetter(publicProperty);
+            var protectedPropertySetter = PropertyHelper.MakeFastPropertySetter(protectedProperty);
+
+            // Act
+            publicPropertySetter(instance, "TestPublic");
+            protectedPropertySetter(instance, "TestProtected");
+
+            // Assert
+            Assert.Equal("TestPublic", instance.PropA);
+            Assert.Equal("TestProtected", instance.GetPropProtected());
+        }
+
+        [Fact]
+        public void MakeFastPropertySetter_SetsPropertyValues_ForOverridenProperties()
+        {
+            // Arrange
+            var instance = new DerivedClassWithOverride();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var property = typeInfo.GetDeclaredProperty("PropA");
+            var propertySetter = PropertyHelper.MakeFastPropertySetter(property);
+
+            // Act
+            propertySetter(instance, "Test value");
+
+            // Assert
+            Assert.Equal("OverridenTest value", instance.PropA);
+        }
+
+        [Fact]
+        public void MakeFastPropertySetter_SetsPropertyValues_ForNewedProperties()
+        {
+            // Arrange
+            var instance = new DerivedClassWithNew();
+            var typeInfo = instance.GetType().GetTypeInfo();
+            var property = typeInfo.GetDeclaredProperty("PropB");
+            var propertySetter = PropertyHelper.MakeFastPropertySetter(property);
+
+            // Act
+            propertySetter(instance, "Test value");
+
+            // Assert
+            Assert.Equal("NewedTest value", instance.PropB);
+        }
+
+        [Fact]
+        public void MakeFastPropertyGetter_ReferenceType_ForNullObject_Throws()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(BaseClass))
+                .Single(p => p.Name == nameof(BaseClass.PropA));
+
+            var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property);
+
+            // Act & Assert
+            Assert.Throws<NullReferenceException>(() => accessor(null));
+        }
+
+        [Fact]
+        public void MakeFastPropertyGetter_ValueType_ForNullObject_Throws()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(MyProperties))
+                .Single(p => p.Name == nameof(MyProperties.StringProp));
+
+            var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property);
+
+            // Act & Assert
+            Assert.Throws<NullReferenceException>(() => accessor(null));
+        }
+
+        [Fact]
+        public void MakeNullSafeFastPropertyGetter_ReferenceType_Success()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(BaseClass))
+                .Single(p => p.Name == nameof(BaseClass.PropA));
+
+            var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
+
+            // Act
+            var value = accessor(new BaseClass() { PropA = "Hi" });
+
+            // Assert
+            Assert.Equal("Hi", value);
+        }
+
+        [Fact]
+        public void MakeNullSafeFastPropertyGetter_ValueType_Success()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(MyProperties))
+                .Single(p => p.Name == nameof(MyProperties.StringProp));
+
+            var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
+
+            // Act
+            var value = accessor(new MyProperties() { StringProp = "Hi" });
+
+            // Assert
+            Assert.Equal("Hi", value);
+        }
+
+        [Fact]
+        public void MakeNullSafeFastPropertyGetter_ReferenceType_ForNullObject_ReturnsNull()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(BaseClass))
+                .Single(p => p.Name == nameof(BaseClass.PropA));
+
+            var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
+
+            // Act
+            var value = accessor(null);
+
+            // Assert
+            Assert.Null(value);
+        }
+
+        [Fact]
+        public void MakeNullSafeFastPropertyGetter_ValueType_ForNullObject_ReturnsNull()
+        {
+            // Arrange
+            var property = PropertyHelper
+                .GetProperties(typeof(MyProperties))
+                .Single(p => p.Name == nameof(MyProperties.StringProp));
+
+            var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
+
+            // Act
+            var value = accessor(null);
+
+            // Assert
+            Assert.Null(value);
+        }
+
+        public static TheoryData<object, KeyValuePair<string, object>> IgnoreCaseTestData
+        {
+            get
+            {
+                return new TheoryData<object, KeyValuePair<string, object>>
+                {
+                    {
+                        new
+                        {
+                            selected = true,
+                            SeLeCtEd = false
+                        },
+                        new KeyValuePair<string, object>("selected", false)
+                    },
+                    {
+                        new
+                        {
+                            SeLeCtEd = false,
+                            selected = true
+                        },
+                        new KeyValuePair<string, object>("SeLeCtEd", true)
+                    },
+                    {
+                        new
+                        {
+                            SelECTeD = false,
+                            SeLECTED = true
+                        },
+                        new KeyValuePair<string, object>("SelECTeD", true)
+                    }
+                };
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(IgnoreCaseTestData))]
+        public void ObjectToDictionary_IgnoresPropertyCase(object testObject,
+                                                           KeyValuePair<string, object> expectedEntry)
+        {
+            // Act
+            var result = PropertyHelper.ObjectToDictionary(testObject);
+
+            // Assert
+            var entry = Assert.Single(result);
+            Assert.Equal(expectedEntry, entry);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_WithNullObject_ReturnsEmptyDictionary()
+        {
+            // Arrange
+            object value = null;
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(0, dictValues.Count);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_WithPlainObjectType_ReturnsEmptyDictionary()
+        {
+            // Arrange
+            var value = new object();
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(0, dictValues.Count);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_WithPrimitiveType_LooksUpPublicProperties()
+        {
+            // Arrange
+            var value = "test";
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(1, dictValues.Count);
+            Assert.Equal(4, dictValues["Length"]);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_WithAnonymousType_LooksUpProperties()
+        {
+            // Arrange
+            var value = new { test = "value", other = 1 };
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(2, dictValues.Count);
+            Assert.Equal("value", dictValues["test"]);
+            Assert.Equal(1, dictValues["other"]);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_ReturnsCaseInsensitiveDictionary()
+        {
+            // Arrange
+            var value = new { TEST = "value", oThEr = 1 };
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(2, dictValues.Count);
+            Assert.Equal("value", dictValues["test"]);
+            Assert.Equal(1, dictValues["other"]);
+        }
+
+        [Fact]
+        public void ObjectToDictionary_ReturnsInheritedProperties()
+        {
+            // Arrange
+            var value = new ThreeDPoint() { X = 5, Y = 10, Z = 17 };
+
+            // Act
+            var dictValues = PropertyHelper.ObjectToDictionary(value);
+
+            // Assert
+            Assert.NotNull(dictValues);
+            Assert.Equal(3, dictValues.Count);
+            Assert.Equal(5, dictValues["X"]);
+            Assert.Equal(10, dictValues["Y"]);
+            Assert.Equal(17, dictValues["Z"]);
+        }
+
+        private class Point
+        {
+            public int X { get; set; }
+            public int Y { get; set; }
+        }
+
+        private class ThreeDPoint : Point
+        {
+            public int Z { get; set; }
+        }
+
+        private class Static
+        {
+            public static int Prop2 { get; set; }
+            public int Prop5 { get; set; }
+        }
+
+        private struct MyProperties
+        {
+            public int IntProp { get; set; }
+            public string StringProp { get; set; }
+        }
+
+        private class SetOnly
+        {
+            public int Prop2 { set { } }
+            public int Prop6 { get; set; }
+        }
+
+        private class PrivateProperties
+        {
+            public int Prop1 { get; set; }
+            protected int Prop2 { get; set; }
+            private int Prop3 { get; set; }
+        }
+
+        public class BaseClass
+        {
+            public string PropA { get; set; }
+
+            protected string PropProtected { get; set; }
+
+            public string GetPropProtected()
+            {
+                return PropProtected;
+            }
+        }
+
+        public class DerivedClass : BaseClass
+        {
+            public string PropB { get; set; }
+        }
+
+        public class BaseClassWithVirtual
+        {
+            public virtual string PropA { get; set; }
+            public string PropB { get; set; }
+        }
+
+        public class DerivedClassWithNew : BaseClassWithVirtual
+        {
+            private string _value = "Newed";
+
+            public new string PropB
+            {
+                get { return _value; }
+                set { _value = "Newed" + value; }
+            }
+        }
+
+        public class DerivedClassWithOverride : BaseClassWithVirtual
+        {
+            private string _value = "Overriden";
+
+            public override string PropA
+            {
+                get { return _value; }
+                set { _value = "Overriden" + value; }
+            }
+        }
+
+        private class DerivedClassWithNonReadableProperties : BaseClassWithVirtual
+        {
+            public string this[int index]
+            {
+                get { return string.Empty; }
+                set { }
+            }
+
+            public int Visible { get; set; }
+
+            private string NotVisible { get; set; }
+
+            public string NotVisible2 { private get; set; }
+
+            public string NotVisible3
+            {
+                set { }
+            }
+
+            public static string NotVisible4 { get; set; }
+        }
+
+        private struct MyStruct
+        {
+            public string Foo { get; set; }
+        }
+
+        private class BaseHiddenProperty
+        {
+            public int Id { get; set; }
+        }
+
+        private class DerivedHiddenProperty : BaseHiddenProperty
+        {
+            public new string Id { get; set; }
+
+            public string Name { get; set; }
+        }
+
+        private class DerivedHiddenProperty2 : DerivedHiddenProperty
+        {
+            public new Guid Id { get; set; }
+
+            public new string Name { get; private set; }
+        }
+    }
+}

+ 93 - 0
src/Shared/test/Shared.Tests/SecurityHelperTests.cs

@@ -0,0 +1,93 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Principal;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class SecurityHelperTests
+    {
+        [Fact]
+        public void AddingToAnonymousIdentityDoesNotKeepAnonymousIdentity()
+        {
+            var user = SecurityHelper.MergeUserPrincipal(new ClaimsPrincipal(), new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), new string[0]));
+
+            Assert.NotNull(user);
+            Assert.Equal("Alpha", user.Identity.AuthenticationType);
+            Assert.Equal("Test1", user.Identity.Name);
+            Assert.IsAssignableFrom<ClaimsPrincipal>(user);
+            Assert.IsAssignableFrom<ClaimsIdentity>(user.Identity);
+            Assert.Single(user.Identities);
+        }
+
+        [Fact]
+        public void AddingExistingIdentityChangesDefaultButPreservesPrior()
+        {
+            ClaimsPrincipal user = new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), null);
+
+            Assert.Equal("Alpha", user.Identity.AuthenticationType);
+            Assert.Equal("Test1", user.Identity.Name);
+
+            user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test2", "Beta"), new string[0]));
+
+            Assert.Equal("Beta", user.Identity.AuthenticationType);
+            Assert.Equal("Test2", user.Identity.Name);
+
+            user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0]));
+
+            Assert.Equal("Gamma", user.Identity.AuthenticationType);
+            Assert.Equal("Test3", user.Identity.Name);
+
+            Assert.Equal(3, user.Identities.Count());
+            Assert.Equal("Test3", user.Identities.Skip(0).First().Name);
+            Assert.Equal("Test2", user.Identities.Skip(1).First().Name);
+            Assert.Equal("Test1", user.Identities.Skip(2).First().Name);
+        }
+
+        [Fact]
+        public void AddingPreservesNewIdentitiesAndDropsEmpty()
+        {
+            var existingPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
+            var identityNoAuthTypeWithClaim = new ClaimsIdentity();
+            identityNoAuthTypeWithClaim.AddClaim(new Claim("identityNoAuthTypeWithClaim", "yes"));
+            existingPrincipal.AddIdentity(identityNoAuthTypeWithClaim);
+            var identityEmptyWithAuthType = new ClaimsIdentity("empty");
+            existingPrincipal.AddIdentity(identityEmptyWithAuthType);
+
+            Assert.False(existingPrincipal.Identity.IsAuthenticated);
+
+            var newPrincipal = new ClaimsPrincipal();
+            var newEmptyIdentity = new ClaimsIdentity();
+            var identityTwo = new ClaimsIdentity("yep");
+            newPrincipal.AddIdentity(newEmptyIdentity);
+            newPrincipal.AddIdentity(identityTwo);
+
+            var user = SecurityHelper.MergeUserPrincipal(existingPrincipal, newPrincipal);
+
+            // Preserve newPrincipal order
+            Assert.False(user.Identity.IsAuthenticated);
+            Assert.Null(user.Identity.Name);
+
+            Assert.Equal(4, user.Identities.Count());
+            Assert.Equal(newEmptyIdentity, user.Identities.Skip(0).First());
+            Assert.Equal(identityTwo, user.Identities.Skip(1).First());
+            Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First());
+            Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First());
+
+            // This merge should drop newEmptyIdentity since its empty
+            user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0]));
+
+            Assert.Equal("Gamma", user.Identity.AuthenticationType);
+            Assert.Equal("Test3", user.Identity.Name);
+
+            Assert.Equal(4, user.Identities.Count());
+            Assert.Equal("Test3", user.Identities.Skip(0).First().Name);
+            Assert.Equal(identityTwo, user.Identities.Skip(1).First());
+            Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First());
+            Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First());
+        }
+    }
+}

+ 345 - 0
src/Shared/test/Shared.Tests/StackTraceHelperTest.cs

@@ -0,0 +1,345 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Microsoft.Extensions.StackTrace.Sources;
+using ThrowingLibrary;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class StackTraceHelperTest
+    {
+        [Fact]
+        public void StackTraceHelper_IncludesLineNumbersForFiles()
+        {
+            // Arrange
+            Exception exception = null;
+            try
+            {
+                // Throwing an exception in the current assembly always seems to populate the full stack
+                // trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used
+                // on desktop.
+                Thrower.Throw();
+            }
+            catch (Exception ex)
+            {
+                exception = ex;
+            }
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            Assert.Collection(stackFrames,
+                frame =>
+                {
+                    Assert.Contains("Thrower.cs", frame.FilePath);
+                    Assert.Equal(17, frame.LineNumber);
+                },
+                frame =>
+                {
+                    Assert.Contains("StackTraceHelperTest.cs", frame.FilePath);
+                });
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods()
+        {
+            // Arrange
+            var exception = Record.Exception(() => GenericMethod<string>(null));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod<T>(T val)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters()
+        {
+            // Arrange
+            var exception = Record.Exception(() => MethodWithOutParameter(out var value));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters()
+        {
+            // Arrange
+            var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter<TVal>(string a, out TVal value)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters()
+        {
+            // Arrange
+            var value = 0;
+            var exception = Record.Exception(() => MethodWithRefParameter(ref value));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters()
+        {
+            // Arrange
+            var value = 0;
+            var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter<TVal>(ref TVal value)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters()
+        {
+            // Arrange
+            var value = 0;
+            var exception = Record.Exception(() => MethodWithNullableParameter(value));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable<int> value)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes()
+        {
+            // Arrange
+            var exception = Record.Exception(() => new GenericClass<int>().Throw(0));
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.Throw(T parameter)", methods[0]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_ProducesReadableOutput()
+        {
+            // Arrange
+            var expectedCallStack = new List<string>()
+            {
+                "Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()",
+                "string.Join(string separator, IEnumerable<string> values)",
+                "Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.GenericMethod<V>(ref V value)",
+                "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)",
+                "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync<TValue>(TValue value)",
+                "Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)",
+                "Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()",
+            };
+
+            Exception exception = null;
+            try
+            {
+                Method("test");
+            }
+            catch (Exception ex)
+            {
+                exception = ex;
+            }
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+            var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray();
+
+            // Assert
+            Assert.Equal(expectedCallStack, methodNames);
+        }
+
+        [Fact]
+        public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute()
+        {
+            // Arrange
+            var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute());
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute()
+        {
+            // Arrange
+            var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute());
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
+        }
+
+        [Fact]
+        public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute()
+        {
+            // Arrange
+            var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw());
+
+            // Act
+            var stackFrames = StackTraceHelper.GetFrames(exception);
+
+            // Assert
+            var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
+            Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]);
+        }
+
+        [Fact]
+        public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies()
+        {
+            // Arrange
+            var action = (Action)Expression.Lambda(
+                Expression.Throw(
+                    Expression.New(typeof(Exception)))).Compile();
+            var exception = Record.Exception(action);
+
+            // Act
+            var frames = StackTraceHelper.GetFrames(exception).ToArray();
+
+            // Assert
+            var frame = frames[0];
+            Assert.Null(frame.FilePath);
+            Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString());
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        async Task<string> MethodAsync(int value)
+        {
+            await Task.Delay(0);
+            return GenericClass<byte>.GenericMethod(ref value);
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        async Task<string> MethodAsync<TValue>(TValue value)
+        {
+            return await MethodAsync(1);
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        string Method(string value)
+        {
+            return MethodAsync(value).GetAwaiter().GetResult();
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        static IEnumerable<string> Iterator()
+        {
+            yield return "Success";
+            throw new Exception();
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void MethodWithOutParameter(out int value) => throw new Exception();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void MethodWithGenericOutParameter<TVal>(string a, out TVal value) => throw new Exception();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void MethodWithRefParameter(ref int value) => throw new Exception();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void MethodWithGenericRefParameter<TVal>(ref TVal value) => throw new Exception();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void MethodWithNullableParameter(int? value) => throw new Exception();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw();
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic();
+
+        class GenericClass<T>
+        {
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            public static string GenericMethod<V>(ref V value)
+            {
+                var returnVal = "";
+                for (var i = 0; i < 10; i++)
+                {
+                    returnVal += string.Join(", ", Iterator());
+                }
+                return returnVal;
+            }
+
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            public void Throw(T parameter) => throw new Exception();
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        private void GenericMethod<T>(T val) where T : class => throw new Exception();
+
+        private class StackTraceHiddenAttribute : Attribute
+        {
+        }
+
+        [StackTraceHidden]
+        private class TypeWithStackTraceHiddenAttribute
+        {
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            public void Throw() => ThrowCore();
+
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            public static void ThrowStatic() => ThrowCore();
+        }
+
+        private class TypeWithMethodWithStackTraceHiddenAttribute
+        {
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            [StackTraceHidden]
+            public void MethodWithStackTraceHiddenAttribute()
+            {
+                ThrowCore();
+            }
+
+            [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+            public void Throw() => MethodWithStackTraceHiddenAttribute();
+        }
+
+        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
+        private static void ThrowCore() => throw new Exception();
+    }
+}

+ 113 - 0
src/Shared/test/Shared.Tests/WebEncodersTests.cs

@@ -0,0 +1,113 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+    public class WebEncodersTests
+    {
+        [Theory]
+        [InlineData("", 1, 0)]
+        [InlineData("", 0, 1)]
+        [InlineData("0123456789", 9, 2)]
+        [InlineData("0123456789", Int32.MaxValue, 2)]
+        [InlineData("0123456789", 9, -1)]
+        public void Base64UrlDecode_BadOffsets(string input, int offset, int count)
+        {
+            // Act & assert
+            Assert.ThrowsAny<ArgumentException>(() =>
+            {
+                var retVal = WebEncoders.Base64UrlDecode(input, offset, count);
+            });
+        }
+
+        [Theory]
+        [InlineData("x")]
+        [InlineData("(x)")]
+        public void Base64UrlDecode_MalformedInput(string input)
+        {
+            // Act & assert
+            Assert.Throws<FormatException>(() =>
+            {
+                var retVal = WebEncoders.Base64UrlDecode(input);
+            });
+        }
+
+        [Theory]
+        [InlineData("", "")]
+        [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")]
+        [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")]
+        [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")]
+        public void Base64UrlEncode_And_Decode(string base64Input, string expectedBase64Url)
+        {
+            // Arrange
+            byte[] input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray();
+
+            // Act & assert - 1
+            string actualBase64Url = WebEncoders.Base64UrlEncode(input, 3, input.Length - 5); // also helps test offsets
+            Assert.Equal(expectedBase64Url, actualBase64Url);
+
+            // Act & assert - 2
+            // Verify that values round-trip
+            byte[] roundTripped = WebEncoders.Base64UrlDecode("xx" + actualBase64Url + "yyy", 2, actualBase64Url.Length); // also helps test offsets
+            string roundTrippedAsBase64 = Convert.ToBase64String(roundTripped);
+            Assert.Equal(roundTrippedAsBase64, base64Input);
+        }
+
+        [Theory]
+        [InlineData("", "")]
+        [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")]
+        [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")]
+        [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")]
+        public void Base64UrlEncode_And_Decode_WithBufferOffsets(string base64Input, string expectedBase64Url)
+        {
+            // Arrange
+            var input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray();
+            var buffer = new char[30];
+            var output = new char[30];
+            for (var i = 0; i < buffer.Length; i++)
+            {
+                buffer[i] = '^';
+                output[i] = '^';
+            }
+
+            // Act 1
+            var numEncodedChars =
+                WebEncoders.Base64UrlEncode(input, offset: 3, output: output, outputOffset: 4, count: input.Length - 5);
+
+            // Assert 1
+            var encodedString = new string(output, startIndex: 4, length: numEncodedChars);
+            Assert.Equal(expectedBase64Url, encodedString);
+
+            // Act 2
+            var roundTripInput = new string(output);
+            var roundTripped =
+                WebEncoders.Base64UrlDecode(roundTripInput, offset: 4, buffer: buffer, bufferOffset: 5, count: numEncodedChars);
+
+            // Assert 2, verify that values round-trip
+            var roundTrippedAsBase64 = Convert.ToBase64String(roundTripped);
+            Assert.Equal(roundTrippedAsBase64, base64Input);
+        }
+
+        [Theory]
+        [InlineData(0, 1, 0)]
+        [InlineData(0, 0, 1)]
+        [InlineData(10, 9, 2)]
+        [InlineData(10, Int32.MaxValue, 2)]
+        [InlineData(10, 9, -1)]
+        public void Base64UrlEncode_BadOffsets(int inputLength, int offset, int count)
+        {
+            // Arrange
+            byte[] input = new byte[inputLength];
+
+            // Act & assert
+            Assert.ThrowsAny<ArgumentException>(() =>
+            {
+                var retVal = WebEncoders.Base64UrlEncode(input, offset, count);
+            });
+        }
+    }
+}

+ 20 - 0
src/Shared/test/testassets/ThrowingLibrary/Thrower.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace ThrowingLibrary
+{
+    // Throwing an exception in the current assembly always seems to populate the full stack
+    // trace regardless of symbol type. This type exists to simulate an exception thrown
+    // across assemblies which is the typical use case for StackTraceHelper.
+    public static class Thrower
+    {
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static void Throw()
+        {
+            throw new DivideByZeroException();
+        }
+    }
+}

+ 8 - 0
src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <DebugType>portable</DebugType>
+  </PropertyGroup>
+
+</Project>

+ 1 - 0
src/SignalR/Directory.Build.props

@@ -17,5 +17,6 @@
     <PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 	  <LangVersion>latest</LangVersion>
+    <SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
   </PropertyGroup>
 </Project>

+ 1 - 5
src/SignalR/build/dependencies.props

@@ -65,7 +65,6 @@
     <MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
     <MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreToolsPackageVersion>
     <MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
-    <MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
     <MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
     <MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.1</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
     <MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.1</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
@@ -78,10 +77,7 @@
     <MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
     <MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
     <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
-    <MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
     <MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
-    <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
     <MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
-    <MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
   </PropertyGroup>
-</Project>
+</Project>

+ 5 - 2
src/SignalR/src/Microsoft.AspNetCore.Http.Connections/Microsoft.AspNetCore.Http.Connections.csproj

@@ -14,6 +14,11 @@
     <Compile Include="..\Common\DuplexPipe.cs" Link="DuplexPipe.cs" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Http.Connections.Common\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
   </ItemGroup>
@@ -24,8 +29,6 @@
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(MicrosoftAspNetCoreWebSocketsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" PrivateAssets="All" />
     <PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" PrivateAssets="All" />
     <PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
   </ItemGroup>

+ 5 - 2
src/SignalR/src/Microsoft.AspNetCore.SignalR.Core/Microsoft.AspNetCore.SignalR.Core.csproj

@@ -6,6 +6,11 @@
     <RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
+    <Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
     <ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Protocols.Json\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
@@ -15,8 +20,6 @@
     <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftAspNetCoreAuthorizationPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
     <PackageReference Include="System.Reflection.Emit" Version="$(SystemReflectionEmitPackageVersion)" />
     <PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
   </ItemGroup>

+ 1 - 0
src/Templating/Directory.Build.props

@@ -13,5 +13,6 @@
     <RepositoryUrl>https://github.com/aspnet/AspNetCore</RepositoryUrl>
     <RepositoryType>git</RepositoryType>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
   </PropertyGroup>
 </Project>

+ 0 - 2
src/Templating/build/dependencies.props

@@ -15,7 +15,6 @@
     <MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
     <MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
     <MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>
-    <MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
     <MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreCookiePolicyPackageVersion>
     <MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.1</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>
     <MicrosoftAspNetCoreHttpsPolicyPackageVersion>2.1.1</MicrosoftAspNetCoreHttpsPolicyPackageVersion>
@@ -35,7 +34,6 @@
     <MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
     <MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreToolsPackageVersion>
     <MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
-    <MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
     <MicrosoftNETCoreApp21PackageVersion>2.1.5</MicrosoftNETCoreApp21PackageVersion>
     <MicrosoftNETSdkRazorPackageVersion>2.1.2-rtm-30913</MicrosoftNETSdkRazorPackageVersion>
     <MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>

+ 2 - 2
src/Templating/test/Templates.Test/Templates.Test.csproj

@@ -6,15 +6,15 @@
 
   <ItemGroup>
     <EmbeddedResource Include="template-baselines.json" />
+    <Compile Include="$(SharedSourceRoot)Process\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
   </ItemGroup>
 
   <ItemGroup>
     <PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
     <PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkPackageVersion)" />
-    <PackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
     <PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
     <PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" PrivateAssets="All" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
     <PackageReference Include="Selenium.Firefox.WebDriver" Version="$(SeleniumFirefoxWebDriverPackageVersion)" />
     <PackageReference Include="Selenium.Support" Version="$(SeleniumSupportPackageVersion)" NoWarn="NU1701" />

+ 1 - 0
src/Tools/Directory.Build.props

@@ -4,6 +4,7 @@
   <PropertyGroup>
     <BaseIntermediateOutputPath>$(RepositoryRoot)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
     <BaseOutputPath>$(RepositoryRoot)bin\$(MSBuildProjectName)\</BaseOutputPath>
+    <ToolSharedSourceRoot>$(MSBuildThisFileDirectory)Shared\</ToolSharedSourceRoot>
   </PropertyGroup>
 
 </Project>

+ 1 - 1
src/Tools/FirstRunCertGenerator/src/Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj

@@ -8,7 +8,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <Reference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" />
+    <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
   </ItemGroup>
 
 </Project>

+ 6 - 0
src/Tools/FirstRunCertGenerator/src/Properties/AssemblyInfo.cs

@@ -0,0 +1,6 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

+ 300 - 0
src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs

@@ -0,0 +1,300 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.AspNetCore.Certificates.Generation.Tests
+{
+    public class CertificateManagerTests
+    {
+        public CertificateManagerTests(ITestOutputHelper output)
+        {
+            Output = output;
+        }
+
+        public const string TestCertificateSubject = "CN=aspnet.test";
+
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
+        {
+            try
+            {
+                // Arrange
+                const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
+                var manager = new CertificateManager();
+
+                manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+                {
+                    manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
+                }
+
+                // Act
+                DateTimeOffset now = DateTimeOffset.UtcNow;
+                now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+                var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject);
+
+                // Assert
+                Assert.Equal(EnsureCertificateResult.Succeeded, result);
+                Assert.True(File.Exists(CertificateName));
+
+                var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
+                Assert.NotNull(exportedCertificate);
+                Assert.False(exportedCertificate.HasPrivateKey);
+
+                var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false);
+                var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject);
+                Assert.True(httpsCertificate.HasPrivateKey);
+                Assert.Equal(TestCertificateSubject, httpsCertificate.Subject);
+                Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer);
+                Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName);
+                Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value);
+
+                Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore);
+                Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter);
+                Assert.Contains(
+                    httpsCertificate.Extensions.OfType<X509Extension>(),
+                    e => e is X509BasicConstraintsExtension basicConstraints &&
+                        basicConstraints.Critical == true &&
+                        basicConstraints.CertificateAuthority == false &&
+                        basicConstraints.HasPathLengthConstraint == false &&
+                        basicConstraints.PathLengthConstraint == 0);
+
+                Assert.Contains(
+                    httpsCertificate.Extensions.OfType<X509Extension>(),
+                    e => e is X509KeyUsageExtension keyUsage &&
+                        keyUsage.Critical == true &&
+                        keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment);
+
+                Assert.Contains(
+                    httpsCertificate.Extensions.OfType<X509Extension>(),
+                    e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage &&
+                        enhancedKeyUsage.Critical == true &&
+                        enhancedKeyUsage.EnhancedKeyUsages.OfType<Oid>().Single() is Oid keyUsage &&
+                        keyUsage.Value == "1.3.6.1.5.5.7.3.1");
+
+                // Subject alternative name
+                Assert.Contains(
+                    httpsCertificate.Extensions.OfType<X509Extension>(),
+                    e => e.Critical == true &&
+                        e.Oid.Value == "2.5.29.17");
+
+                // ASP.NET HTTPS Development certificate extension
+                Assert.Contains(
+                    httpsCertificate.Extensions.OfType<X509Extension>(),
+                    e => e.Critical == false &&
+                        e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" &&
+                        Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate");
+
+                Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
+
+            }
+            catch (Exception e)
+            {
+                Output.WriteLine(e.Message);
+                ListCertificates(Output);
+                throw;
+            }
+        }
+
+        private void ListCertificates(ITestOutputHelper output)
+        {
+            using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+            {
+                store.Open(OpenFlags.ReadOnly);
+                var certificates = store.Certificates;
+                foreach (var certificate in certificates)
+                {
+                    Output.WriteLine($"Certificate: '{Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}'.");
+                    certificate.Dispose();
+                }
+
+                store.Close();
+            }
+        }
+
+        [Fact]
+        public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
+        {
+            // Arrange
+            const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
+            var certificatePassword = Guid.NewGuid().ToString();
+
+            var manager = new CertificateManager();
+
+            manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
+            }
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+            manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
+
+            var httpsCertificate = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject);
+
+            // Act
+            var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
+
+            // Assert
+            Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result);
+            Assert.True(File.Exists(CertificateName));
+
+            var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
+            Assert.NotNull(exportedCertificate);
+            Assert.True(exportedCertificate.HasPrivateKey);
+
+
+            Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
+        }
+
+        [Fact(Skip = "Requires user interaction")]
+        public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows()
+        {
+            var manager = new CertificateManager();
+
+            manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
+            }
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+            var trustFailed = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject);
+
+            Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed);
+        }
+
+        [Fact(Skip = "Requires user interaction")]
+        public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates()
+        {
+            var manager = new CertificateManager();
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+            manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject);
+
+            manager.CleanupHttpsCertificates(TestCertificateSubject);
+
+            Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
+            }
+        }
+
+        [Fact]
+        public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
+        {
+            // Arrange
+            const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
+            var manager = new CertificateManager();
+
+            manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
+            }
+
+            // Act
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+            var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject);
+
+            // Assert
+            Assert.Equal(EnsureCertificateResult.Succeeded, result);
+            Assert.True(File.Exists(CertificateName));
+
+            var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
+            Assert.NotNull(exportedCertificate);
+            Assert.False(exportedCertificate.HasPrivateKey);
+
+            var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false);
+            var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject);
+            Assert.True(identityCertificate.HasPrivateKey);
+            Assert.Equal(TestCertificateSubject, identityCertificate.Subject);
+            Assert.Equal(TestCertificateSubject, identityCertificate.Issuer);
+            Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName);
+            Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value);
+
+            Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore);
+            Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter);
+            Assert.Contains(
+                identityCertificate.Extensions.OfType<X509Extension>(),
+                e => e is X509BasicConstraintsExtension basicConstraints &&
+                    basicConstraints.Critical == true &&
+                    basicConstraints.CertificateAuthority == false &&
+                    basicConstraints.HasPathLengthConstraint == false &&
+                    basicConstraints.PathLengthConstraint == 0);
+
+            Assert.Contains(
+                identityCertificate.Extensions.OfType<X509Extension>(),
+                e => e is X509KeyUsageExtension keyUsage &&
+                    keyUsage.Critical == true &&
+                    keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature);
+
+            Assert.Contains(
+                identityCertificate.Extensions.OfType<X509Extension>(),
+                e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage &&
+                    enhancedKeyUsage.Critical == true &&
+                    enhancedKeyUsage.EnhancedKeyUsages.OfType<Oid>().Single() is Oid keyUsage &&
+                    keyUsage.Value == "1.3.6.1.5.5.7.3.1");
+
+            // ASP.NET Core Identity Json Web Token signing development certificate
+            Assert.Contains(
+                identityCertificate.Extensions.OfType<X509Extension>(),
+                e => e.Critical == false &&
+                    e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" &&
+                    Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate");
+
+            Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
+        }
+
+        [Fact]
+        public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
+        {
+            // Arrange
+            const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
+            var certificatePassword = Guid.NewGuid().ToString();
+
+            var manager = new CertificateManager();
+
+            manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
+            }
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
+            manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
+
+            var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject);
+
+            // Act
+            var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
+
+            // Assert
+            Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result);
+            Assert.True(File.Exists(CertificateName));
+
+            var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
+            Assert.NotNull(exportedCertificate);
+            Assert.True(exportedCertificate.HasPrivateKey);
+
+            Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString());
+        }
+    }
+}

+ 11 - 0
src/Tools/FirstRunCertGenerator/test/Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests.csproj

@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+  	<Reference Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" />
+  </ItemGroup>
+
+</Project>

+ 0 - 0
src/Tools/shared/src/CliContext.cs → src/Tools/Shared/CommandLine/CliContext.cs


+ 0 - 0
src/Tools/shared/src/CommandLineApplicationExtensions.cs → src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs


+ 0 - 0
src/Tools/shared/src/ConsoleReporter.cs → src/Tools/Shared/CommandLine/ConsoleReporter.cs


+ 0 - 0
src/Tools/shared/src/DebugHelper.cs → src/Tools/Shared/CommandLine/DebugHelper.cs


+ 0 - 0
src/Tools/shared/src/Ensure.cs → src/Tools/Shared/CommandLine/Ensure.cs


+ 0 - 0
src/Tools/shared/src/IConsole.cs → src/Tools/Shared/CommandLine/IConsole.cs


+ 0 - 0
src/Tools/shared/src/IReporter.cs → src/Tools/Shared/CommandLine/IReporter.cs


+ 0 - 0
src/Tools/shared/src/NullReporter.cs → src/Tools/Shared/CommandLine/NullReporter.cs


Some files were not shown because too many files changed in this diff