Browse Source

Merge branch 'release/2.1' into release/2.2

Nate McMaster 7 years ago
parent
commit
c1c7d711b6
100 changed files with 7172 additions and 236 deletions
  1. 7 28
      .azure/pipelines/fast-pr-validation.yml
  2. 1 0
      .azure/pipelines/jobs/default-build.yml
  3. 0 8
      .gitmodules
  4. 20 0
      Directory.Build.props
  5. 38 0
      Directory.Build.targets
  6. 1 0
      build/CodeSign.props
  7. 1 1
      build/CodeSign.targets
  8. 1 3
      build/PackageArchive.targets
  9. 42 10
      build/SharedFx.targets
  10. 0 1
      build/artifacts.props
  11. 0 4
      build/buildorder.props
  12. 1 1
      build/dependencies.folderbuilds.props
  13. 4 0
      build/dependencies.props
  14. 1 0
      build/external-dependencies.props
  15. 14 0
      build/repo.props
  16. 49 9
      build/repo.targets
  17. 0 4
      build/submodules.props
  18. 1 0
      build/tasks/RepoTasks.csproj
  19. 41 0
      docs/PreparingPatchUpdates.md
  20. 116 0
      eng/Baseline.props
  21. 55 0
      eng/Dependencies.props
  22. 14 0
      eng/PatchConfig.props
  23. 18 0
      eng/ProjectReferences.props
  24. 14 0
      eng/targets/CSharp.Common.props
  25. 5 0
      eng/targets/CSharp.Common.targets
  26. 30 0
      eng/targets/Packaging.targets
  27. 135 0
      eng/targets/ResolveReferences.targets
  28. 15 0
      eng/tools/BaselineGenerator/BaselineGenerator.csproj
  29. 138 0
      eng/tools/BaselineGenerator/Program.cs
  30. 10 0
      eng/tools/BaselineGenerator/README.md
  31. 15 0
      eng/tools/BaselineGenerator/baseline.xml
  32. 3 0
      eng/tools/Directory.Build.props
  33. 2 0
      eng/tools/Directory.Build.targets
  34. 34 0
      eng/tools/tools.sln
  35. 1 1
      global.json
  36. 2 2
      korebuild-lock.txt
  37. 0 1
      modules/HtmlAbstractions
  38. 0 1
      modules/JsonPatch
  39. 0 1
      scripts/UpdateDependencies.ps1
  40. 1 1
      src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj
  41. 2 2
      src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj
  42. 3 6
      src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj
  43. 3 6
      src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj
  44. 2 5
      src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj
  45. 3 6
      src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj
  46. 1 1
      src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj
  47. 1 1
      src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj
  48. 2 2
      src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj
  49. 9 12
      src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj
  50. 5 5
      src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj
  51. 0 19
      src/DataProtection/Directory.Build.props
  52. 2 9
      src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj
  53. 2 5
      src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj
  54. 2 5
      src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj
  55. 3 2
      src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj
  56. 2 5
      src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj
  57. 5 8
      src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj
  58. 2 5
      src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj
  59. 0 12
      src/DataProtection/build/repo.props
  60. 5 8
      src/DataProtection/samples/AzureBlob/AzureBlob.csproj
  61. 6 9
      src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj
  62. 4 7
      src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj
  63. 2 2
      src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj
  64. 1 1
      src/DataProtection/samples/NonDISample/NonDISample.csproj
  65. 4 7
      src/DataProtection/samples/Redis/Redis.csproj
  66. 0 10
      src/DataProtection/version.props
  67. 49 0
      src/Features/JsonPatch/src/Adapters/AdapterFactory.cs
  68. 22 0
      src/Features/JsonPatch/src/Adapters/IAdapterFactory.cs
  69. 112 0
      src/Features/JsonPatch/src/Adapters/IObjectAdapter.cs
  70. 32 0
      src/Features/JsonPatch/src/Adapters/IObjectAdapterWithTest.cs
  71. 348 0
      src/Features/JsonPatch/src/Adapters/ObjectAdapter.cs
  72. 76 0
      src/Features/JsonPatch/src/Converters/JsonPatchDocumentConverter.cs
  73. 64 0
      src/Features/JsonPatch/src/Converters/TypedJsonPatchDocumentConverter.cs
  74. 38 0
      src/Features/JsonPatch/src/Exceptions/JsonPatchException.cs
  75. 30 0
      src/Features/JsonPatch/src/Helpers/GetValueResult.cs
  76. 43 0
      src/Features/JsonPatch/src/Helpers/JsonPatchProperty.cs
  77. 16 0
      src/Features/JsonPatch/src/IJsonPatchDocument.cs
  78. 17 0
      src/Features/JsonPatch/src/Internal/ConversionResult.cs
  79. 75 0
      src/Features/JsonPatch/src/Internal/ConversionResultProvider.cs
  80. 245 0
      src/Features/JsonPatch/src/Internal/DictionaryAdapterOfTU.cs
  81. 243 0
      src/Features/JsonPatch/src/Internal/DynamicObjectAdapter.cs
  82. 16 0
      src/Features/JsonPatch/src/Internal/ErrorReporter.cs
  83. 51 0
      src/Features/JsonPatch/src/Internal/IAdapter.cs
  84. 349 0
      src/Features/JsonPatch/src/Internal/ListAdapter.cs
  85. 79 0
      src/Features/JsonPatch/src/Internal/ObjectVisitor.cs
  86. 92 0
      src/Features/JsonPatch/src/Internal/ParsedPath.cs
  87. 32 0
      src/Features/JsonPatch/src/Internal/PathHelpers.cs
  88. 236 0
      src/Features/JsonPatch/src/Internal/PocoAdapter.cs
  89. 271 0
      src/Features/JsonPatch/src/JsonPatchDocument.cs
  90. 884 0
      src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs
  91. 50 0
      src/Features/JsonPatch/src/JsonPatchError.cs
  92. 17 0
      src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj
  93. 82 0
      src/Features/JsonPatch/src/Operations/Operation.cs
  94. 76 0
      src/Features/JsonPatch/src/Operations/OperationBase.cs
  95. 94 0
      src/Features/JsonPatch/src/Operations/OperationOfT.cs
  96. 16 0
      src/Features/JsonPatch/src/Operations/OperationType.cs
  97. 7 0
      src/Features/JsonPatch/src/Properties/AssemblyInfo.cs
  98. 338 0
      src/Features/JsonPatch/src/Properties/Resources.Designer.cs
  99. 186 0
      src/Features/JsonPatch/src/Resources.resx
  100. 1985 0
      src/Features/JsonPatch/src/baseline.netcore.json

+ 7 - 28
.azure/pipelines/fast-pr-validation.yml

@@ -6,39 +6,18 @@ trigger:
 jobs:
 - template: jobs/default-build.yml
   parameters:
+    jobName: PR_FastCheck
+    jobDisplayName: Fast Check
     agentOs: Windows
-    jobName: Windows_FastCheck
-    jobDisplayName: Windows - Fast Check
     buildArgs: "/t:FastCheck"
-    artifacts:
-      publish: false
 - template: jobs/default-build.yml
   parameters:
-    agentOs: Windows
-    jobName: Compile_Windows
+    jobName: Windows_Build
     jobDisplayName: "Build: Windows"
-    buildArgs: "/p:SkipTests=true"
-    artifacts:
-      publish: false
-- job: RepoBuilds
-  pool:
-    vmImage: vs2017-win2016
-  strategy:
-    maxParallel: 3
-    matrix:
-      DataProtection:
-        _FolderName: DataProtection
-      WebSockets:
-        _FolderName: WebSockets
-  steps:
-  - script: src/$(_FolderName)/build.cmd -ci
-    displayName: Run src/$(_FolderName)/build.cmd
-  - task: PublishTestResults@2
-    displayName: Publish test results
-    condition: always()
-    inputs:
-      testRunner: vstest
-      testResultsFiles: 'src/$(_FolderName)/artifacts/logs/**/*.trx'
+    agentOs: Windows
+    beforeBuild:
+    - powershell: "& ./src/IISIntegration/tools/UpdateIISExpressCertificate.ps1"
+      displayName: Setup IISExpress test certificates
 - template: jobs/iisintegration-job.yml
   parameters:
     TestGroupName: IIS

+ 1 - 0
.azure/pipelines/jobs/default-build.yml

@@ -63,6 +63,7 @@ jobs:
 - job: ${{ coalesce(parameters.jobName, parameters.agentOs) }}
   displayName: ${{ coalesce(parameters.jobDisplayName, parameters.agentOs) }}
   dependsOn: ${{ parameters.dependsOn }}
+  timeoutInMinutes: 90
   workspace:
     clean: all
   strategy:

+ 0 - 8
.gitmodules

@@ -42,10 +42,6 @@
 	path = modules/Hosting
 	url = https://github.com/aspnet/Hosting.git
 	branch = release/2.2
-[submodule "modules/HtmlAbstractions"]
-	path = modules/HtmlAbstractions
-	url = https://github.com/aspnet/HtmlAbstractions.git
-	branch = release/2.2
 [submodule "modules/HttpAbstractions"]
 	path = modules/HttpAbstractions
 	url = https://github.com/aspnet/HttpAbstractions.git
@@ -66,10 +62,6 @@
 	path = modules/JavaScriptServices
 	url = https://github.com/aspnet/JavaScriptServices.git
 	branch = release/2.2
-[submodule "modules/JsonPatch"]
-	path = modules/JsonPatch
-	url = https://github.com/aspnet/JsonPatch.git
-	branch = release/2.2
 [submodule "modules/KestrelHttpServer"]
 	path = modules/KestrelHttpServer
 	url = https://github.com/aspnet/KestrelHttpServer.git

+ 20 - 0
Directory.Build.props

@@ -17,6 +17,8 @@
       replace PackageLicenseUrl with PackageLicenseExpression.
     -->
     <NoWarn>$(NoWarn);NU5125</NoWarn>
+    <!-- Suppress warnings about using SemVer 2.0. -->
+    <NoWarn>$(NoWarn);NU5105</NoWarn>
 
     <!-- Contact email address for NuGet packages and Linux installers. -->
     <MaintainerEmail>[email protected]</MaintainerEmail>
@@ -49,6 +51,16 @@
     <BuildProjectReferences Condition=" '$(NoBuild)' == 'true' ">false</BuildProjectReferences>
   </PropertyGroup>
 
+  <!-- Certificate names used for Authenticode signing. -->
+  <PropertyGroup Condition=" '$(DisableCodeSigning)' != 'true' ">
+    <AssemblySigningCertName>Microsoft400</AssemblySigningCertName>
+    <AssemblySigning3rdPartyCertName>3PartySHA2</AssemblySigning3rdPartyCertName>
+    <PowerShellSigningCertName>Microsoft400</PowerShellSigningCertName>
+    <PackageSigningCertName>NuGet</PackageSigningCertName>
+    <VsixSigningCertName>VsixSHA2</VsixSigningCertName>
+    <JarSigningCertName>MicrosoftJAR</JarSigningCertName>
+  </PropertyGroup>
+
   <Import Project="build\external-dependencies.props" />
   <Import Project="build\sources.props" />
 
@@ -75,8 +87,16 @@
     <SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
   </PropertyGroup>
 
+  <PropertyGroup>
+    <StandardTestTfms>netcoreapp2.2;net461</StandardTestTfms>
+  </PropertyGroup>
+
+  <Import Project="eng\Dependencies.props" />
+  <Import Project="eng\PatchConfig.props" />
+  <Import Project="eng\ProjectReferences.props" />
   <Import Project="eng\targets\RuntimeIdentifiers.props" />
   <Import Project="eng\targets\Cpp.Common.props"  Condition="'$(MSBuildProjectExtension)' == '.vcxproj'" />
+  <Import Project="eng\targets\CSharp.Common.props"  Condition="'$(MSBuildProjectExtension)' == '.csproj'" />
   <Import Project="eng\targets\SharedFx.Common.props"  Condition="'$(MSBuildProjectExtension)' == '.shfxproj'" />
   <Import Project="eng\targets\Wix.Common.props"  Condition="'$(MSBuildProjectExtension)' == '.wixproj'" />
 

+ 38 - 0
Directory.Build.targets

@@ -1,9 +1,46 @@
 <Project>
   <!-- Properties which should be set after the project has been evaluated -->
+  <PropertyGroup Condition=" '$(MSBuildProjectExtension)' == '.csproj' ">
+    <PackageId Condition=" '$(PackageId)' == '' ">$(AssemblyName)</PackageId>
+    <IsPackable Condition="'$(IsPackable)' == '' AND ( '$(IsTestProject)' == 'true' OR '$(IsTestAssetProject)' == 'true' OR '$(IsBenchmarkProject)' == 'true' OR '$(IsSampleProject)' == 'true' ) ">false</IsPackable>
+  </PropertyGroup>
+
+  <Import Project="eng\Baseline.props" />
+
+  <PropertyGroup Condition=" '$(IsPackable)' != 'false' AND '$(AspNetCorePatchVersion)' != '0' ">
+    <!-- Always include framework metapackages in patch updates. -->
+    <IsPackageInThisPatch Condition="'$(IsFrameworkMetapackage)' == 'true'">true</IsPackageInThisPatch>
+    <IsPackageInThisPatch Condition="'$(IsPackageInThisPatch)' == ''">$(PackagesInPatch.Contains(' $(PackageId);'))</IsPackageInThisPatch>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(IsPackable)' != 'false' AND '$(IsServicingBuild)' == 'true' ">
+    <!-- Used to distinguish between packages building -->
+    <IsPackableInNonServicingBuild>true</IsPackableInNonServicingBuild>
+    <!-- Suppress creation of .nupkg for servicing builds. -->
+    <IsPackable Condition=" '$(IsPackageInThisPatch)' != 'true' ">false</IsPackable>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(IsPackageInThisPatch)' != 'true' AND '$(BaselinePackageVersion)' != '' ">
+    <!-- This keeps assembly and package versions consistent across patches. If a package is not included in a patch, its version should stay at the baseline. -->
+    <AssemblyVersion>$(BaselinePackageVersion).0</AssemblyVersion>
+    <!--
+      Ideally, we would also set the project version to match the baseline in case NuGet turns a ProjectReference into a nuspec depenendency, but
+      NuGet does not currently handle conflicts between packages and projects which have the same package id/version.
+      See https://github.com/NuGet/Home/issues/6795.
+
+      Because we still use static analysis to scrape versions, only set this during static analysis, which can be detected by checking for both NoBuild and DesignTimeBuild.
+    -->
+    <Version Condition="'$(NoBuild)' == 'true' AND '$(DesignTimeBuild)' == 'true'">$(BaselinePackageVersion)</Version>
+    <PackageVersion Condition="'$(NoBuild)' == 'true' AND '$(DesignTimeBuild)' == 'true'">$(BaselinePackageVersion)</PackageVersion>
+  </PropertyGroup>
+
   <PropertyGroup>
     <!-- Implementation projects are the projects which produce nuget packages or shipping assemblies. -->
     <IsImplementationProject Condition=" '$(IsImplementationProject)' == '' AND '$(IsTestAssetProject)' != 'true' AND '$(IsTestProject)' != 'true' AND '$(IsBenchmarkProject)' != 'true' AND '$(IsSampleProject)' != 'true' ">true</IsImplementationProject>
 
+    <!-- Suppress KoreBuild warnings about the mismatch of repo version and local project version. The versioning in this mega repo is sufficiently complicated that KoreBuild's validation isn't helpful. -->
+    <VerifyVersion>false</VerifyVersion>
+
     <EnableApiCheck Condition=" '$(EnableApiCheck)' != '' ">$(IsImplementationProject)</EnableApiCheck>
     <IsPackable Condition="'$(IsPackable)' == '' AND '$(IsImplementationProject)' == 'true' ">true</IsPackable>
     <IsPackable Condition="'$(IsPackable)' == '' ">false</IsPackable>
@@ -17,6 +54,7 @@
   </PropertyGroup>
 
   <Import Project="eng\targets\Cpp.Common.targets"  Condition="'$(MSBuildProjectExtension)' == '.vcxproj'" />
+  <Import Project="eng\targets\CSharp.Common.targets"  Condition="'$(MSBuildProjectExtension)' == '.csproj'" />
   <Import Project="eng\targets\SharedFx.Common.targets"  Condition="'$(MSBuildProjectExtension)' == '.shfxproj'" />
   <Import Project="eng\targets\Wix.Common.targets"  Condition="'$(MSBuildProjectExtension)' == '.wixproj'" />
 </Project>

+ 1 - 0
build/CodeSign.props

@@ -56,6 +56,7 @@
       <FilesToSign Include="Microsoft.Extensions.Options.DataAnnotations.dll"               Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
       <FilesToSign Include="Microsoft.Extensions.Options.dll"                               Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
       <FilesToSign Include="Microsoft.Extensions.Primitives.dll"                            Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
+      <FilesToSign Include="Microsoft.Extensions.WebEncoders.dll"                           Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
 
     <!-- These files came from partner teams. They have to be re-signed because we crossgen them and redistributable them in our installers. -->
 

+ 1 - 1
build/CodeSign.targets

@@ -4,7 +4,7 @@
     <CodeSignDependsOn>$(CodeSignDependsOn);CollectFileSignInfo</CodeSignDependsOn>
   </PropertyGroup>
 
-  <Target Name="CollectFileSignInfo" DependsOnTargets="_PrepareRepositories">
+  <Target Name="CollectFileSignInfo" DependsOnTargets="_PrepareRepositories;GetProjectArtifactInfo;GetFxProjectArtifactInfo">
 
     <ItemGroup>
       <_RepositoryProject Remove="@(_RepositoryProject)" />

+ 1 - 3
build/PackageArchive.targets

@@ -8,9 +8,7 @@
     <ArchiveProjects Include="$(RepositoryRoot)src\PackageArchive\Archive.*\*.*proj" />
   </ItemGroup>
 
-  <Target Name="BuildFallbackArchive" DependsOnTargets="ResolveRepoInfo;GeneratePropsFiles">
-
-
+  <Target Name="BuildFallbackArchive" DependsOnTargets="GetProjectArtifactInfo;GetFxProjectArtifactInfo;ResolveRepoInfo;GeneratePropsFiles">
     <PropertyGroup>
       <ArchiveBuildProps>
         DotNetRestoreSourcePropsPath=$(GeneratedRestoreSourcesPropsPath);

+ 42 - 10
build/SharedFx.targets

@@ -2,19 +2,20 @@
   <PropertyGroup>
     <UnitTestFxProject>$(RepositoryRoot)src\Framework\Framework.UnitTests\Framework.UnitTests.csproj</UnitTestFxProject>
     <UnitTestFxProject>$([MSBuild]::NormalizePath($(UnitTestFxProject)))</UnitTestFxProject>
-    <CodeSignDependsOn>$(CodeSignDependsOn);GetSharedFxFilesToSign</CodeSignDependsOn>
+    <CodeSignDependsOn Condition="'$(_ProjectsOnly)' != 'true'">$(CodeSignDependsOn);GetSharedFxFilesToSign</CodeSignDependsOn>
     <BuildSharedFxDependsOn>_BuildSharedFxProjects;TestSharedFx</BuildSharedFxDependsOn>
     <BuildSharedFxDependsOn Condition="'$(TestOnly)' != 'true'">$(BuildSharedFxDependsOn);CodeSign</BuildSharedFxDependsOn>
     <RedistNetCorePath>$(IntermediateDir)ar\$(SharedFxRid)\</RedistNetCorePath>
+    <GetArtifactInfoDependsOn>$(GetArtifactInfo);GetFxProjectArtifactInfo</GetArtifactInfoDependsOn>
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectToBuild Include="$(RepositoryRoot)src\Framework\**\*.pkgproj" />
-    <ProjectToBuild Include="$(RepositoryRoot)src\Framework\**\*.shfxproj" />
-    <ProjectToBuild Include="$(UnitTestFxProject)" />
-    <ProjectToBuild Condition=" '$(BuildRuntimeArchive)' != 'false' " Include="$(RepositoryRoot)src\Installers\Archive\*.*proj" />
-    <ProjectToBuild Condition=" '$(SharedFxRid)' == 'linux-x64' AND '$(LinuxInstallerType)' == 'deb' " Include="$(RepositoryRoot)src\Installers\Debian\*.*proj" />
-    <ProjectToBuild Condition=" '$(SharedFxRid)' == 'linux-x64' AND '$(LinuxInstallerType)' == 'rpm' " Include="$(RepositoryRoot)src\Installers\Rpm\*.*proj" />
+    <FxProjectToBuild Include="$(RepositoryRoot)src\Framework\**\*.pkgproj" />
+    <FxProjectToBuild Include="$(RepositoryRoot)src\Framework\**\*.shfxproj" />
+    <FxProjectToBuild Include="$(UnitTestFxProject)" />
+    <FxProjectToBuild Condition=" '$(BuildRuntimeArchive)' != 'false' " Include="$(RepositoryRoot)src\Installers\Archive\*.*proj" />
+    <FxProjectToBuild Condition=" '$(SharedFxRid)' == 'linux-x64' AND '$(LinuxInstallerType)' == 'deb' " Include="$(RepositoryRoot)src\Installers\Debian\*.*proj" />
+    <FxProjectToBuild Condition=" '$(SharedFxRid)' == 'linux-x64' AND '$(LinuxInstallerType)' == 'rpm' " Include="$(RepositoryRoot)src\Installers\Rpm\*.*proj" />
   </ItemGroup>
 
   <Target Name="BuildSharedFx" DependsOnTargets="$(BuildSharedFxDependsOn)" />
@@ -43,7 +44,7 @@
   <Target Name="_BuildSharedFxProjects" DependsOnTargets="GeneratePropsFiles;ResolveCommitHash">
 
     <PropertyGroup>
-      <_RestoreGraphProjectInput>@(ProjectToBuild)</_RestoreGraphProjectInput>
+      <_RestoreGraphProjectInput>@(FxProjectToBuild)</_RestoreGraphProjectInput>
       <SharedFxBuildProperties>
         $(BuildProperties);
         SharedFxRid=$(SharedFxRid);
@@ -56,11 +57,11 @@
              Targets="Restore"
              Properties="$(SharedFxBuildProperties);RestoreGraphProjectInput=$(_RestoreGraphProjectInput);_DummyTarget=Restore" />
 
-    <MSBuild Projects="@(ProjectToBuild)"
+    <MSBuild Projects="@(FxProjectToBuild)"
              Properties="$(SharedFxBuildProperties)"
              BuildInParallel="true" />
 
-    <MSBuild Projects="@(ProjectToBuild)"
+    <MSBuild Projects="@(FxProjectToBuild)"
              Targets="Pack"
              Properties="$(SharedFxBuildProperties);NoBuild=true"
              BuildInParallel="true"
@@ -98,4 +99,35 @@
     <Error Text="SharedFx.UnitTests failed with exit code '$(VsTestExitCode)'." Condition=" $(VsTestExitCode) != 0 " />
   </Target>
 
+  <Target Name="GetFxProjectArtifactInfo" Returns="@(ArtifactInfo)">
+    <PropertyGroup>
+      <_InspectionTargetsFile>$(MSBuildProjectDirectory)\Project.Inspection.targets</_InspectionTargetsFile>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <_Temp Remove="@(_Temp)" />
+    </ItemGroup>
+
+    <MSBuild Targets="GetArtifactInfo"
+             Projects="@(FxProjectToBuild)"
+             Condition="@(FxProjectToBuild->Count()) != 0"
+             Properties="$(BuildProperties);DesignTimeBuild=true;EnableApiCheck=false;NoBuild=true;CustomAfterMicrosoftCommonTargets=$(_InspectionTargetsFile);CustomAfterMicrosoftCommonCrossTargetingTargets=$(_InspectionTargetsFile)"
+             BuildInParallel="$(BuildInParallel)"
+             RemoveProperties="$(_BuildPropertiesToRemove)">
+
+      <Output TaskParameter="TargetOutputs" ItemName="_Temp" />
+    </MSBuild>
+
+    <ItemGroup>
+      <!-- Output from this target may include items representing assemblies inside the nupkg. -->
+      <ArtifactInfo Include="@(_Temp)" Condition="'%(_Temp.Container)' == ''" />
+
+      <!-- Nupkgs or assemblies in the nupkg that should be signed -->
+      <FilesToSign Include="@(_Temp)" Condition=" '%(_Temp.ShouldBeSigned)' == 'true' " />
+
+      <!-- Nupkgs or assemblies in the nupkg that should not be signed -->
+      <FilesToExcludeFromSigning Include="@(_Temp)" Condition=" '%(_Temp.ShouldBeSigned)' != 'true' " />
+    </ItemGroup>
+  </Target>
+
 </Project>

+ 0 - 1
build/artifacts.props

@@ -199,7 +199,6 @@
     <PackageArtifact Include="Microsoft.Extensions.Identity.Stores" Category="ship" />
     <PackageArtifact Include="Microsoft.Extensions.Localization.Abstractions" Category="ship" />
     <PackageArtifact Include="Microsoft.Extensions.Localization" Category="ship" />
-    <PackageArtifact Include="Microsoft.Extensions.WebEncoders" Category="ship" />
     <PackageArtifact Include="Microsoft.Net.Http.Headers" Category="ship" />
     <PackageArtifact Include="Microsoft.NET.Sdk.Razor" Category="ship" />
     <PackageArtifact Include="Microsoft.Owin.Security.Interop" Category="ship" />

+ 0 - 4
build/buildorder.props

@@ -7,9 +7,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <RepositoryBuildOrder Include="JsonPatch" Order="1" />
     <RepositoryBuildOrder Include="DotNetTools" Order="1" />
-    <RepositoryBuildOrder Include="HtmlAbstractions" Order="1" />
     <RepositoryBuildOrder Include="Razor" Order="6" />
     <RepositoryBuildOrder Include="HttpAbstractions" Order="6" />
     <RepositoryBuildOrder Include="HttpClientFactory" Order="6" />
@@ -18,7 +16,6 @@
     <RepositoryBuildOrder Include="EntityFrameworkCore" Order="8" />
     <RepositoryBuildOrder Include="HttpSysServer" Order="8" />
     <RepositoryBuildOrder Include="BrowserLink" Order="8" />
-    <RepositoryBuildOrder Include="DataProtection" Order="9" RootPath="$(RepositoryRoot)src\DataProtection\" />
     <RepositoryBuildOrder Include="BasicMiddleware" Order="9" />
     <RepositoryBuildOrder Include="Antiforgery" Order="10" />
     <RepositoryBuildOrder Include="IISIntegration" Order="10" RootPath="$(RepositoryRoot)src\IISIntegration\" />
@@ -30,7 +27,6 @@
     <RepositoryBuildOrder Include="Routing" Order="12" />
     <RepositoryBuildOrder Include="Diagnostics" Order="12" />
     <RepositoryBuildOrder Include="Localization" Order="13" />
-    <RepositoryBuildOrder Include="WebSockets" Order="13" RootPath="$(RepositoryRoot)src\WebSockets\" />
     <RepositoryBuildOrder Include="Security" Order="13" />
     <RepositoryBuildOrder Include="MetaPackages" Order="13" />
     <RepositoryBuildOrder Include="Mvc" Order="14" />

+ 1 - 1
build/dependencies.folderbuilds.props

@@ -1,7 +1,7 @@
 <!-- This file is temporary while we rework dependencies management. It is used when building individual folders as if they were repos. -->
 <Project>
   <PropertyGroup>
-    <InternalAspNetCoreSdkPackageVersion>2.2.0-preview2-20181108.4</InternalAspNetCoreSdkPackageVersion>
+    <InternalAspNetCoreSdkPackageVersion>2.2.1-build-20181114.3</InternalAspNetCoreSdkPackageVersion>
     <MicrosoftAspNetCoreAspNetCoreModulePackageVersion>2.2.0</MicrosoftAspNetCoreAspNetCoreModulePackageVersion>
     <MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
     <MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion>2.2.0</MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion>

+ 4 - 0
build/dependencies.props

@@ -19,6 +19,7 @@
     <MicrosoftAspNetCoreBuildToolsApiCheckPackageVersion>$(KoreBuildVersion)</MicrosoftAspNetCoreBuildToolsApiCheckPackageVersion>
     <InternalAspNetCoreSdkPackageVersion>$(KoreBuildVersion)</InternalAspNetCoreSdkPackageVersion>
     <InternalAspNetCoreSiteExtensionSdkPackageVersion>$(KoreBuildVersion)</InternalAspNetCoreSiteExtensionSdkPackageVersion>
+    <InternalAspNetCoreSdkPackageVersion Condition=" '$(KoreBuildVersion)' == '' ">2.2.1-build-20181114.3</InternalAspNetCoreSdkPackageVersion>
   </PropertyGroup>
 
   <!-- These are package versions that should not be overridden or updated by automation. -->
@@ -30,6 +31,8 @@
     <MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
     <MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.2.0</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
     <MicrosoftAspNetCoreTestingPackageVersion>2.2.0</MicrosoftAspNetCoreTestingPackageVersion>
+    <MicrosoftEntityFrameworkCoreInMemoryPackageVersion>2.2.0</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
+    <MicrosoftEntityFrameworkCorePackageVersion>2.2.0</MicrosoftEntityFrameworkCorePackageVersion>
     <MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.2.0</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
     <MicrosoftExtensionsCachingAbstractionsPackageVersion>2.2.0</MicrosoftExtensionsCachingAbstractionsPackageVersion>
     <MicrosoftExtensionsCachingMemoryPackageVersion>2.2.0</MicrosoftExtensionsCachingMemoryPackageVersion>
@@ -85,6 +88,7 @@
     <MicrosoftExtensionsStackTraceSourcesPackageVersion>2.2.0</MicrosoftExtensionsStackTraceSourcesPackageVersion>
     <MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.2.0</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
     <MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.2.0</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
+    <MicrosoftExtensionsWebEncodersPackageVersion>2.2.0</MicrosoftExtensionsWebEncodersPackageVersion>
     <MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.2.0</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
 
     <!-- 3rd party dependencies -->

+ 1 - 0
build/external-dependencies.props

@@ -72,6 +72,7 @@
     <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)" />
   </ItemGroup>
 
   <ItemGroup>

+ 14 - 0
build/repo.props

@@ -4,6 +4,8 @@
   <PropertyGroup>
     <!-- This repo does not have solutions to build -->
     <DisableDefaultTargets>true</DisableDefaultTargets>
+    <DisableDefaultItems>true</DisableDefaultItems>
+    <BuildSolutions>false</BuildSolutions>
 
     <SkipTests>false</SkipTests>
     <SkipTests Condition="'$(CompileOnly)' == 'true'">true</SkipTests>
@@ -46,6 +48,18 @@
     <SharedFrameworkName Include="Microsoft.AspNetCore.App" />
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectToExclude Include="$(RepositoryRoot)src\Middleware\WebSockets\samples\**\*.csproj" />
+
+    <ProjectToBuild Include="
+                      $(RepositoryRoot)src\Features\JsonPatch\**\*.*proj;
+                      $(RepositoryRoot)src\DataProtection\**\*.*proj;
+                      $(RepositoryRoot)src\Html\**\*.*proj;
+                      $(RepositoryRoot)src\Middleware\**\*.*proj;
+                      "
+                    Exclude="@(ProjectToExclude)" />
+  </ItemGroup>
+
   <!-- Properties for publishing -->
   <PropertyGroup>
     <!-- myget = non-orchestrated builds -->

+ 49 - 9
build/repo.targets

@@ -15,20 +15,60 @@
     <GeneratedRestoreSourcesPropsPath>$(IntermediateDir)sources.g.props</GeneratedRestoreSourcesPropsPath>
     <GeneratedBrandingPropsPath>$(IntermediateDir)branding.g.props</GeneratedBrandingPropsPath>
 
-    <PrepareDependsOn>SetTeamCityBuildNumberToVersion;$(PrepareDependsOn);VerifyPackageArtifactConfig;VerifyExternalDependencyConfig;PrepareOutputPaths</PrepareDependsOn>
+    <PrepareDependsOn>SetTeamCityBuildNumberToVersion;$(PrepareDependsOn)</PrepareDependsOn>
+    <PrepareDependsOn Condition="'$(_ProjectsOnly)' != 'true'">$(PrepareDependsOn);VerifyPackageArtifactConfig;VerifyExternalDependencyConfig;PrepareOutputPaths</PrepareDependsOn>
     <CleanDependsOn>$(CleanDependsOn);CleanArtifacts;CleanRepoArtifacts</CleanDependsOn>
-    <RestoreDependsOn>$(RestoreDependsOn);InstallDotNet</RestoreDependsOn>
-    <CompileDependsOn>$(CompileDependsOn);BuildRepositories;BuildSharedFx</CompileDependsOn>
-    <PackageDependsOn Condition="'$(TestOnly)' != 'true'">$(PackageDependsOn);CheckExpectedPackagesExist;CodeSign</PackageDependsOn>
-    <TestDependsOn>$(TestDependsOn);_TestRepositories</TestDependsOn>
-    <GetArtifactInfoDependsOn>$(GetArtifactInfoDependsOn);ResolveRepoInfo</GetArtifactInfoDependsOn>
+    <RestoreDependsOn>$(RestoreDependsOn);InstallDotNet;RestoreProjects</RestoreDependsOn>
+    <CompileDependsOn>$(CompileDependsOn);BuildProjects</CompileDependsOn>
+    <CompileDependsOn Condition="'$(_ProjectsOnly)' != 'true'">$(CompileDependsOn);PackProjects;BuildRepositories;BuildSharedFx</CompileDependsOn>
+    <PackageDependsOn Condition=" '$(_ProjectsOnly)' == 'true'">$(PackageDependsOn);PackProjects</PackageDependsOn>
+    <PackageDependsOn Condition="'$(TestOnly)' != 'true' AND '$(_ProjectsOnly)' != 'true'">$(PackageDependsOn);CheckExpectedPackagesExist</PackageDependsOn>
+    <PackageDependsOn Condition="'$(TestOnly)' != 'true'">$(PackageDependsOn);CodeSign</PackageDependsOn>
+    <TestDependsOn>$(TestDependsOn);TestProjects</TestDependsOn>
+    <TestDependsOn Condition="'$(_ProjectsOnly)' != 'true'">$(TestDependsOn);_TestRepositories</TestDependsOn>
+    <GetArtifactInfoDependsOn>$(GetArtifactInfoDependsOn);GetProjectArtifactInfo</GetArtifactInfoDependsOn>
+    <GetArtifactInfoDependsOn  Condition="'$(_ProjectsOnly)' != 'true'">$(GetArtifactInfoDependsOn);ResolveRepoInfo</GetArtifactInfoDependsOn>
   </PropertyGroup>
 
   <Target Name="PrepareOutputPaths">
     <MakeDir Directories="$(ArtifactsDir);$(BuildDir)" />
   </Target>
 
-  <Target Name="ResolveRepoInfo" DependsOnTargets="_PrepareRepositories;GetLineupPackageInfo">
+  <Target Name="GenerateProjectList" DependsOnTargets="ResolveProjects">
+    <MSBuild Projects="@(ProjectToBuild)"
+             Targets="GetReferencesProvided"
+             BuildInParallel="true"
+             SkipNonexistentTargets="true"
+             SkipNonexistentProjects="true" >
+
+      <Output TaskParameter="TargetOutputs" ItemName="_ProjectReferenceProvider"/>
+    </MSBuild>
+
+    <PropertyGroup>
+      <ProjectListFile>$(MSBuildThisFileDirectory)..\eng\ProjectReferences.props</ProjectListFile>
+      <ProjectListContent>
+      <![CDATA[
+<!-- This file is automatically generated. Run `build.cmd /t:GenerateProjectList` to update. -->
+<Project>
+  <ItemGroup>
+    @(_ProjectReferenceProvider->'<ProjectReferenceProvider Include="%(Identity)" ProjectPath="%24(RepositoryRoot)%(ProjectFileRelativePath)" />', '%0A    ')
+  </ItemGroup>
+</Project>
+      ]]>
+      </ProjectListContent>
+    </PropertyGroup>
+
+    <WriteLinesToFile File="$(ProjectListFile)" Lines="$(ProjectListContent)" Overwrite="true" />
+  </Target>
+
+  <Target Name="_ResolveProjectArtifactsInfoShipped" AfterTargets="GetProjectArtifactInfo">
+    <ItemGroup>
+      <ShippedArtifactInfo Include="@(ArtifactInfo)" Condition="'%(ArtifactInfo.IsShipped)' == 'true'" />
+      <ArtifactInfo Remove="@(ShippedArtifactInfo)" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="ResolveRepoInfo" DependsOnTargets="_ResolveProjectArtifactsInfoShipped;_PrepareRepositories;GetLineupPackageInfo">
     <!-- We need to pass the NETCoreApp package versions to msbuild so that it doesn't complain about us using a different one than it was restored against.  -->
     <PropertyGroup>
       <DesignTimeBuildProps>$(BuildProperties);MicrosoftNETCoreAppPackageVersion=$(MicrosoftNETCoreAppPackageVersion);</DesignTimeBuildProps>
@@ -237,11 +277,11 @@
   <Target Name="BuildRepositories"
      DependsOnTargets="_PrepareRepositories;GeneratePropsFiles;ComputeGraph;_BuildRepositories" />
 
-  <Target Name="ListExpectedPackages" DependsOnTargets="ResolveRepoInfo">
+  <Target Name="ListExpectedPackages" DependsOnTargets="GetProjectArtifactInfo;GetFxProjectArtifactInfo;ResolveRepoInfo">
     <WriteLinesToFile File="$(RepositoryRoot)artifacts\packages.csv" Lines="PackageId,Version;@(ArtifactInfo->WithMetadataValue('ArtifactType', 'NuGetPackage')->'%(PackageId),%(Version)')" Overwrite="true" />
   </Target>
 
-  <Target Name="ComputeGraph" DependsOnTargets="ResolveRepoInfo;GeneratePropsFiles">
+  <Target Name="ComputeGraph" DependsOnTargets="GetProjectArtifactInfo;GetFxProjectArtifactInfo;ResolveRepoInfo;GeneratePropsFiles">
     <ItemGroup>
       <_UndeclaredPackageArtifact Include="%(ArtifactInfo.PackageId)" Condition="'%(ArtifactInfo.ArtifactType)' == 'NuGetPackage'" />
       <_UndeclaredPackageArtifact Remove="@(PackageArtifact)" />

+ 0 - 4
build/submodules.props

@@ -53,18 +53,15 @@
     <ShippedRepository Include="BasicMiddleware" />
     <ShippedRepository Include="BrowserLink" />
     <ShippedRepository Include="CORS" />
-    <ShippedRepository Include="DataProtection" RootPath="$(RepositoryRoot)src\DataProtection\" />
     <ShippedRepository Include="Diagnostics" />
     <ShippedRepository Include="DotNetTools" />
     <ShippedRepository Include="EntityFrameworkCore" />
     <ShippedRepository Include="Hosting" />
-    <ShippedRepository Include="HtmlAbstractions" />
     <ShippedRepository Include="HttpAbstractions" />
     <ShippedRepository Include="HttpClientFactory" />
     <ShippedRepository Include="HttpSysServer" />
     <ShippedRepository Include="Identity" />
     <ShippedRepository Include="JavaScriptServices" />
-    <ShippedRepository Include="JsonPatch" />
     <ShippedRepository Include="KestrelHttpServer" />
     <ShippedRepository Include="Localization" />
     <ShippedRepository Include="MetaPackages" PatchPolicy="CascadeVersions" />
@@ -77,6 +74,5 @@
     <ShippedRepository Include="Session" />
     <ShippedRepository Include="SignalR" />
     <ShippedRepository Include="StaticFiles" />
-    <ShippedRepository Include="WebSockets" RootPath="$(RepositoryRoot)src\WebSockets\" />
   </ItemGroup>
 </Project>

+ 1 - 0
build/tasks/RepoTasks.csproj

@@ -6,6 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Remove="Internal.AspNetCore.Sdk" />
     <PackageReference Include="Microsoft.DotNet.Archive" Version="$(MicrosoftDotNetArchivePackageVersion)" />
     <PackageReference Include="NuGet.Build.Tasks" Version="$(NuGetInMSBuildVersion)" PrivateAssets="All" />
     <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(DevDependency_MicrosoftExtensionsDependencyModelPackageVersion)" PrivateAssets="All" />

+ 41 - 0
docs/PreparingPatchUpdates.md

@@ -0,0 +1,41 @@
+Preparing new servicing updates
+===============================
+
+In order to prepare this repo to build a new servicing update, the following changes need to be made.
+
+* Increment the patch version in the [version.props](/version.props) file in the repository root.
+
+    ```diff
+    -  <AspNetCorePatchVersion>7</AspNetCorePatchVersion>
+    +  <AspNetCorePatchVersion>8</AspNetCorePatchVersion>
+    ```
+
+* Update the package archive baselines. This is used to make sure each build of the package archives we give to Azure only contains new files and does
+  not require overwriting existing files. See [src/PackageArchive/ZipManifestGenerator/](/src/PackageArchive/ZipManifestGenerator/README.md) for instructions on how to run this tool.
+
+* Update the package baselines. This is used to ensure packages keep a consistent set of dependencies between releases.
+  See [eng/tools/BaselineGenerator/](/eng/tools/BaselineGenerator/README.md) for instructions on how to run this tool.
+
+* **For packages with source code in this repo (not a submodule):** Update the list of packages in [eng/PatchConfig.props](/eng/PatchConfig.props) to list which packages should be patching in this release.
+
+* **For packages still building from submodules:** Update the list of repositories which will contain changes in [build/submodules.props](/build/submodules.props).
+
+    * `<ShippedRepository>` items represent repos which were released in a previous patch, and will not contain servicing updates in the next patch.
+    * `<Repository>` items represent repos which will produce new packages in this patch.
+    * It is usually best to move everything to `<ShippedRepository>` and then iteratively add them back to `<Repository>` as new repos receive approval to patch.
+    * Don't change the `PatchPolicy` attribute. The build system uses this to ensure patching rules are obeyed.
+
+* For each repository still listed as a `<Repository>`, update the version.props file in that submodule. For example, https://github.com/aspnet/Templating/pull/824
+
+    * The version prefix in repos should match the version of ASP.NET Core.
+        * Exception: SignalR, which is "1.1", not "2.1".
+    * This leaves holes in versioning, which is okay. This may mean you increment the patch value by more than one. Example:
+        * EF Core ships patches in 2.1.4 as "2.1.4"
+        * EF Core does not ship patches in 2.1.5 or 2.1.6
+        * EF Core ships in 2.1.7, therefore, EFCore's version.props file should jump from 2.1.4 to 2.1.7.
+
+        ```diff
+        <!-- Example change to modules/EntityFrameworkCore/version.props -->
+        - <VersionPrefix>2.1.4</VersionPrefix>
+        + <VersionPrefix>2.1.7</VersionPrefix>
+        ```

+ 116 - 0
eng/Baseline.props

@@ -0,0 +1,116 @@
+<!-- Auto generated. Do not edit manually, use eng/tools/BaselineGenerator/ to recreate. -->
+<Project>
+  <PropertyGroup>
+    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+    <AspNetCoreBaselineVersion>2.2.0</AspNetCoreBaselineVersion>
+  </PropertyGroup>
+  <!-- Package: Microsoft.AspNetCore.Cryptography.Internal-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.Internal' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.Internal' AND '$(TargetFramework)' == 'netstandard2.0' " />
+  <!-- Package: Microsoft.AspNetCore.Cryptography.KeyDerivation-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' AND '$(TargetFramework)' == 'netcoreapp2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[2.2.0, )" />
+  </ItemGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Cryptography.KeyDerivation' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[2.2.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Cryptography.Internal" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Win32.Registry" Version="[4.5.0, )" />
+    <BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.5.0, )" />
+    <BaselinePackageReference Include="System.Security.Principal.Windows" Version="[4.5.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.Abstractions-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Abstractions' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Abstractions' AND '$(TargetFramework)' == 'netstandard2.0' " />
+  <!-- Package: Microsoft.AspNetCore.DataProtection.AzureKeyVault-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureKeyVault' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureKeyVault' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Azure.KeyVault" Version="[2.3.2, )" />
+    <BaselinePackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="[3.19.8, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.AzureStorage-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureStorage' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.AzureStorage' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="WindowsAzure.Storage" Version="[8.1.4, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.EntityFrameworkCore-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.EntityFrameworkCore' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.EntityFrameworkCore" Version="[2.2.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.Extensions-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.Extensions' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[2.2.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.StackExchangeRedis-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.StackExchangeRedis' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.StackExchangeRedis' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="StackExchange.Redis" Version="[2.0.513, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.DataProtection.SystemWeb-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.SystemWeb' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.DataProtection.SystemWeb' AND '$(TargetFramework)' == 'net461' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.DataProtection" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection" Version="[2.2.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.Html.Abstractions-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Html.Abstractions' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Html.Abstractions' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="System.Text.Encodings.Web" Version="[4.5.0, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.JsonPatch-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.JsonPatch' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.JsonPatch' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.CSharp" Version="[4.5.0, )" />
+    <BaselinePackageReference Include="Newtonsoft.Json" Version="[11.0.2, )" />
+  </ItemGroup>
+  <!-- Package: Microsoft.AspNetCore.WebSockets-->
+  <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.WebSockets' ">
+    <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+  </PropertyGroup>
+  <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.WebSockets' AND '$(TargetFramework)' == 'netstandard2.0' ">
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[2.2.0, )" />
+    <BaselinePackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="[4.5.1, )" />
+  </ItemGroup>
+</Project>

+ 55 - 0
eng/Dependencies.props

@@ -0,0 +1,55 @@
+<!-- This file is a work in progress as we merge repos and move content here from build/dependencies.props. -->
+
+<Project>
+  <PropertyGroup>
+    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+  </PropertyGroup>
+
+  <ItemGroup Label="ProdCon dependencies">
+    <!-- These dependencies must use version variables because they may be overriden by ProdCon builds. -->
+    <LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.EntityFrameworkCore" Version="$(MicrosoftEntityFrameworkCorePackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
+    <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" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
+    <LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
+    <LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
+    <LatestPackageReference Include="System.Text.Encodings.Web" Version="$(SystemTextEncodingsWebPackageVersion)" />
+  </ItemGroup>
+
+  <ItemGroup Label="External dependencies">
+    <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. -->
+    <LatestPackageReference Include="Newtonsoft.Json" Version="9.0.1" Condition="'$(UseMSBuildJsonNet)' == 'true'" />
+    <!-- This version should be used by runtime packages -->
+    <LatestPackageReference Include="Newtonsoft.Json" Version="11.0.2" Condition="'$(UseMSBuildJsonNet)' != 'true'" />
+    <LatestPackageReference Include="xunit.abstractions" Version="2.0.1" />
+    <LatestPackageReference Include="xunit.analyzers" Version="0.10.0" />
+    <LatestPackageReference Include="xunit.assert" Version="2.3.1" />
+    <LatestPackageReference Include="xunit.extensibility.core" Version="2.3.1" />
+    <LatestPackageReference Include="xunit.extensibility.execution" Version="2.3.1" />
+    <LatestPackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <LatestPackageReference Include="xunit" Version="2.4.0" />
+  </ItemGroup>
+
+  <ItemGroup Condition=" '$(AddImplicitReferences)' != 'false' AND '$(IsTestProject)' == 'true' AND '$(MSBuildProjectExtension)' == '.csproj' ">
+    <Reference Include="Microsoft.AspNetCore.Testing" />
+    <Reference Include="Microsoft.NET.Test.Sdk" />
+    <Reference Include="Moq" />
+    <Reference Include="xunit" />
+    <Reference Include="xunit.analyzers" />
+    <Reference Include="xunit.runner.visualstudio" />
+  </ItemGroup>
+
+</Project>

+ 14 - 0
eng/PatchConfig.props

@@ -0,0 +1,14 @@
+<Project>
+  <PropertyGroup>
+    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(VersionPrefix)' == '2.2.1' ">
+    <PackagesInPatch>
+      Microsoft.AspNetCore.Server.IIS;
+      Microsoft.AspNetCore.Server.IISIntegration;
+      Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
+    </PackagesInPatch>
+  </PropertyGroup>
+
+</Project>

+ 18 - 0
eng/ProjectReferences.props

@@ -0,0 +1,18 @@
+<!-- This file is automatically generated. Run `build.cmd /t:GenerateProjectList` to update. -->
+<Project>
+  <ItemGroup>
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.JsonPatch" ProjectPath="$(RepositoryRoot)src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepositoryRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" ProjectPath="$(RepositoryRoot)src\DataProtection\AzureKeyVault\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.AzureStorage" ProjectPath="$(RepositoryRoot)src\DataProtection\AzureStorage\src\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepositoryRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" ProjectPath="$(RepositoryRoot)src\DataProtection\Cryptography.KeyDerivation\src\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection" ProjectPath="$(RepositoryRoot)src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" ProjectPath="$(RepositoryRoot)src\DataProtection\EntityFrameworkCore\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Extensions" ProjectPath="$(RepositoryRoot)src\DataProtection\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" ProjectPath="$(RepositoryRoot)src\DataProtection\StackExchangeRedis\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.SystemWeb" ProjectPath="$(RepositoryRoot)src\DataProtection\SystemWeb\src\Microsoft.AspNetCore.DataProtection.SystemWeb.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Html.Abstractions" ProjectPath="$(RepositoryRoot)src\Html\Abstractions\src\Microsoft.AspNetCore.Html.Abstractions.csproj" />
+    <ProjectReferenceProvider Include="Microsoft.AspNetCore.WebSockets" ProjectPath="$(RepositoryRoot)src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj" />
+  </ItemGroup>
+</Project>

+ 14 - 0
eng/targets/CSharp.Common.props

@@ -0,0 +1,14 @@
+<Project>
+
+  <PropertyGroup>
+    <LangVersion>7.2</LangVersion>
+
+    <!-- Instructs the compiler to use SHA256 instead of SHA1 when adding file hashes to PDBs. -->
+    <ChecksumAlgorithm>SHA256</ChecksumAlgorithm>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
+  </ItemGroup>
+
+</Project>

+ 5 - 0
eng/targets/CSharp.Common.targets

@@ -0,0 +1,5 @@
+<Project>
+
+  <Import Project="Packaging.targets" />
+  <Import Project="ResolveReferences.targets" />
+</Project>

+ 30 - 0
eng/targets/Packaging.targets

@@ -0,0 +1,30 @@
+<Project>
+
+  <Target Name="EnsureBaselineIsUpdated"
+          Condition="'$(IsServicingBuild)' == 'true' AND '$(AspNetCoreBaselineVersion)' != '$(PreviousAspNetCoreReleaseVersion)'"
+          BeforeTargets="BeforeBuild">
+    <Error Text="The package baseline ($(AspNetCoreBaselineVersion)) is out of date with the latest release of this repo ($(PreviousAspNetCoreReleaseVersion)).
+                 See $(RepositoryRoot)eng\tools\BaselineGenerator\README.md for instructions on updating this baseline." />
+  </Target>
+
+  <!-- Temporary: this target is used to gather version information to pass to submodule builds. This can be removed after we finish merging submodules. -->
+  <Target Name="GetBaselineArtifactInfo"
+          Condition="'$(IsPackableInNonServicingBuild)' == 'true' AND '$(IsSubfolderBuild)' != 'true' "
+          Returns="@(ArtifactInfo)"
+          BeforeTargets="GetArtifactInfo">
+
+    <PropertyGroup>
+      <FullPackageOutputPath>$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg</FullPackageOutputPath>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <ArtifactInfo Include="$(FullPackageOutputPath)">
+        <ArtifactType>NuGetPackage</ArtifactType>
+        <PackageId>$(PackageId)</PackageId>
+        <Version>$(PackageVersion)</Version>
+        <RepositoryRoot>$(RepositoryRoot)</RepositoryRoot>
+        <IsShipped>true</IsShipped>
+      </ArtifactInfo>
+    </ItemGroup>
+  </Target>
+</Project>

+ 135 - 0
eng/targets/ResolveReferences.targets

@@ -0,0 +1,135 @@
+<Project>
+
+  <PropertyGroup>
+    <ResolveReferencesDependsOn>
+      ResolveCustomReferences;
+      $(ResolveReferencesDependsOn);
+    </ResolveReferencesDependsOn>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <!--
+      Projects should only use the latest package references when:
+        * preparing a new major or minor release (i.e. a non-servicing builds)
+        * when a project is a test or sample project
+        * when a package is releasing a new patch (we like to update external dependencies in patches when possible)
+    -->
+    <UseLatestPackageReferences Condition=" '$(UseLatestPackageReferences)' == '' AND '$(IsServicingBuild)' != 'true'  ">true</UseLatestPackageReferences>
+    <UseLatestPackageReferences Condition=" '$(UseLatestPackageReferences)' == '' AND '$(IsImplementationProject)' != 'true' ">true</UseLatestPackageReferences>
+    <UseLatestPackageReferences Condition=" '$(UseLatestPackageReferences)' == '' AND '$(IsImplementationProject)' == 'true' AND ( '$(IsServicingBuild)' != 'true' OR '$(IsPackable)' == 'true' ) ">true</UseLatestPackageReferences>
+    <UseLatestPackageReferences Condition=" '$(UseLatestPackageReferences)' == '' ">false</UseLatestPackageReferences>
+
+    <!--
+      Projects should only use the project references instead of baseline package references when:
+        * preparing a new major or minor release (i.e. a non-servicing builds)
+        * when a project is a test or sample project
+      We don't use project references between components in servicing builds between compontents to preserve the baseline as much as possible.
+    -->
+    <UseProjectReferences Condition=" '$(UseProjectReferences)' == '' AND '$(IsServicingBuild)' != 'true'  ">true</UseProjectReferences>
+    <UseProjectReferences Condition=" '$(UseProjectReferences)' == '' AND '$(IsImplementationProject)' != 'true' ">true</UseProjectReferences>
+    <UseProjectReferences Condition=" '$(UseProjectReferences)' == '' ">false</UseProjectReferences>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <_ImplicitPackageReference Include="@(PackageReference->WithMetadataValue('IsImplicitlyDefined', 'true'))" />
+    <_ExplicitPackageReference Include="@(PackageReference)" Exclude="@(_ImplicitPackageReference)" />
+    <_ExplicitPackageReference Remove="Internal.AspNetCore.Sdk" />
+
+    <UnusedProjectReferenceProvider Include="@(ProjectReferenceProvider)" Exclude="@(Reference)" />
+
+    <!-- Order matters. Projects should be used when possible instead of packages. -->
+    <_ProjectReferenceByAssemblyName Condition="'$(UseProjectReferences)' == 'true'"
+      Include="@(ProjectReferenceProvider)"
+      Exclude="@(UnusedProjectReferenceProvider)" />
+
+    <ProjectReference Include="@(_ProjectReferenceByAssemblyName->'%(ProjectPath)')" />
+
+    <Reference Remove="@(_ProjectReferenceByAssemblyName)" />
+  </ItemGroup>
+
+  <Target Name="ResolveCustomReferences" BeforeTargets="CollectPackageReferences;ResolveAssemblyReferencesDesignTime;ResolveAssemblyReferences" Condition=" '$(TargetFramework)' != '' ">
+    <ItemGroup>
+      <UnusedBaselinePackageReference Include="@(BaselinePackageReference)" Exclude="@(Reference);@(_ProjectReferenceByAssemblyName)" />
+
+      <!--
+        MSBuild does not provide a way to join on matching identities in a Condition,
+        but you can do a cartesian product of two item groups and filter out mismatched id's in a second pass.
+      -->
+      <_LatestPackageReferenceWithVersion Include="@(Reference)" Condition=" '$(UseLatestPackageReferences)' == 'true' ">
+        <Id>%(LatestPackageReference.Identity)</Id>
+        <Version>%(LatestPackageReference.Version)</Version>
+      </_LatestPackageReferenceWithVersion>
+      <_LatestPackageReferenceWithVersion Remove="@(_LatestPackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " />
+
+      <!-- Remove reference items that have been resolved to a LatestPackageReference item. -->
+      <Reference Remove="@(_LatestPackageReferenceWithVersion)" />
+      <PackageReference Include="@(_LatestPackageReferenceWithVersion)" IsImplicitlyDefined="true" />
+
+      <!-- Resolve references from BaselinePackageReference for servicing builds. -->
+      <_BaselinePackageReferenceWithVersion Include="@(Reference)" Condition=" '$(IsServicingBuild)' == 'true' OR '$(UseLatestPackageReferences)' != 'true' ">
+        <Id>%(BaselinePackageReference.Identity)</Id>
+        <Version>%(BaselinePackageReference.Version)</Version>
+      </_BaselinePackageReferenceWithVersion>
+
+      <_BaselinePackageReferenceWithVersion Remove="@(_BaselinePackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " />
+
+      <!-- Remove reference items that have been resolved to a BaselinePackageReference item. -->
+      <PackageReference Include="@(_BaselinePackageReferenceWithVersion)" IsImplicitlyDefined="true" />
+      <Reference Remove="@(_BaselinePackageReferenceWithVersion)" />
+
+      <!-- For PrivateAssets=All references, like .Sources packages, fallback to LatestPackageReferences. -->
+      <_PrivatePackageReferenceWithVersion Include="@(Reference->WithMetadataValue('PrivateAssets', 'All'))">
+        <Id>%(LatestPackageReference.Identity)</Id>
+        <Version>%(LatestPackageReference.Version)</Version>
+      </_PrivatePackageReferenceWithVersion>
+
+      <_PrivatePackageReferenceWithVersion Remove="@(_PrivatePackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " />
+
+      <!-- Remove reference items that have been resolved to a LatestPackageReference item. -->
+      <PackageReference Include="@(_PrivatePackageReferenceWithVersion)" IsImplicitlyDefined="true" />
+      <Reference Remove="@(_PrivatePackageReferenceWithVersion)" />
+
+      <!-- Free up memory for unnecessary items -->
+      <_LatestPackageReferenceWithVersion Remove="@(_LatestPackageReferenceWithVersion)" />
+      <_BaselinePackageReferenceWithVersion Remove="@(_BaselinePackageReferenceWithVersion)" />
+      <_PrivatePackageReferenceWithVersion Remove="@(_PrivatePackageReferenceWithVersion)" />
+      <_ImplicitPackageReference Remove="@(_ImplicitPackageReference)" />
+    </ItemGroup>
+<!--
+    <Error Condition="@(_ExplicitPackageReference->Count()) != 0"
+           Text="PackageReference items are not allowed. Use &lt;Reference&gt; instead. " /> -->
+
+    <ItemGroup>
+      <_ExplicitPackageReference Remove="@(_ExplicitPackageReference)" />
+    </ItemGroup>
+
+    <Warning Condition="@(UnusedBaselinePackageReference->Count()) != 0"
+             Text="Package references changed since the last release. This could be a breaking change. References removed:%0A - @(UnusedBaselinePackageReference, '%0A -')" />
+
+    <Error Condition="'$(TargetFrameworkIdentifier)' != '.NETFramework' AND '%(Reference.Identity)' != '' AND ! Exists('%(Reference.Identity)')"
+           Code="MSB3245"
+           Text="Could not resolve this reference. Could not locate the package or project for &quot;%(Reference.Identity)&quot;" />
+  </Target>
+
+  <Target Name="GetReferencesProvided" Returns="@(ProvidesReference)">
+    <ItemGroup>
+      <_TargetFramework Remove="@(_TargetFramework)" />
+      <_TargetFramework Include="$(TargetFramework)" Condition="'$(TargetFramework)' != '' "/>
+      <_TargetFramework Include="$(TargetFrameworks)" Condition="'$(TargetFramework)' == '' "/>
+    </ItemGroup>
+
+    <MSBuild Projects="$(MSBuildProjectFullPath)"
+             Targets="_GetReferencesProvided"
+             Properties="TargetFramework=%(_TargetFramework.Identity)">
+      <Output TaskParameter="TargetOutputs" ItemName="ProvidesReference" />
+    </MSBuild>
+  </Target>
+
+  <Target Name="_GetReferencesProvided" Returns="@(ProvidesReference)">
+    <ItemGroup Condition=" '$(IsImplementationProject)' == 'true' OR '$(IsProjectReferenceProvider)' == 'true' ">
+      <ProvidesReference Include="$(AssemblyName)">
+        <ProjectFileRelativePath>$([MSBuild]::MakeRelative($(RepositoryRoot), $(MSBuildProjectFullPath)))</ProjectFileRelativePath>
+      </ProvidesReference>
+    </ItemGroup>
+  </Target>
+</Project>

+ 15 - 0
eng/tools/BaselineGenerator/BaselineGenerator.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <StartArguments>-o "$(MSBuildThisFileDirectory)../../Baseline.props"</StartArguments>
+    <StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="NuGet.Packaging" Version="4.8.0" />
+    <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.0" />
+  </ItemGroup>
+
+</Project>

+ 138 - 0
eng/tools/BaselineGenerator/Program.cs

@@ -0,0 +1,138 @@
+// 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.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Extensions.CommandLineUtils;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+
+namespace PackageBaselineGenerator
+{
+    /// <summary>
+    /// This generates Baseline.props with information about the last RTM release.
+    /// </summary>
+    class Program : CommandLineApplication
+    {
+        static void Main(string[] args)
+        {
+            new Program().Execute(args);
+        }
+
+        private readonly CommandOption _source;
+        private readonly CommandOption _output;
+        private readonly CommandOption _feedv3;
+
+        public Program()
+        {
+            _source = Option("-s|--source <SOURCE>", "The NuGet v2 source of the package to fetch", CommandOptionType.SingleValue);
+            _output = Option("-o|--output <OUT>", "The generated file output path", CommandOptionType.SingleValue);
+            _feedv3 = Option("--v3", "Sources is nuget v3", CommandOptionType.NoValue);
+
+            Invoke = () => Run().GetAwaiter().GetResult();
+        }
+
+        private async Task<int> Run()
+        {
+            var source = _source.HasValue()
+                ? _source.Value()
+                : "https://www.nuget.org/api/v2/package";
+
+            var packageCache = Environment.GetEnvironmentVariable("NUGET_PACKAGES") != null
+                ? Environment.GetEnvironmentVariable("NUGET_PACKAGES")
+                : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
+
+            var tempDir = Path.Combine(Directory.GetCurrentDirectory(), "obj", "tmp");
+            Directory.CreateDirectory(tempDir);
+
+            var input = XDocument.Load(Path.Combine(Directory.GetCurrentDirectory(), "baseline.xml"));
+
+            var output = _output.HasValue()
+                ? _output.Value()
+                : Path.Combine(Directory.GetCurrentDirectory(), "Baseline.props");
+
+            var baselineVersion = input.Root.Attribute("Version").Value;
+
+            var doc = new XDocument(
+                new XComment(" Auto generated. Do not edit manually, use eng/tools/BaselineGenerator/ to recreate. "),
+                new XElement("Project",
+                    new XElement("PropertyGroup",
+                        new XElement("MSBuildAllProjects", "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)"),
+                        new XElement("AspNetCoreBaselineVersion", baselineVersion))));
+
+            var client = new HttpClient();
+
+            foreach (var pkg in input.Root.Descendants("Package"))
+            {
+                var id = pkg.Attribute("Id").Value;
+                var version = pkg.Attribute("Version").Value;
+                var packageFileName = $"{id}.{version}.nupkg";
+                var nupkgPath = Path.Combine(packageCache, id.ToLowerInvariant(), version, packageFileName);
+                if (!File.Exists(nupkgPath))
+                {
+                    nupkgPath = Path.Combine(tempDir, packageFileName);
+                }
+
+                if (!File.Exists(nupkgPath))
+                {
+                    var url = _feedv3.HasValue()
+                        ? $"{source}/{id.ToLowerInvariant()}/{version}/{id.ToLowerInvariant()}.{version}.nupkg"
+                        : $"{source}/{id}/{version}";
+                    Console.WriteLine($"Downloading {url}");
+
+                    var response = await client.GetStreamAsync(url);
+
+                    using (var file = File.Create(nupkgPath))
+                    {
+                        await response.CopyToAsync(file);
+                    }
+                }
+
+
+                using (var reader = new PackageArchiveReader(nupkgPath))
+                {
+                    var first = true;
+                    foreach (var group in reader.NuspecReader.GetDependencyGroups())
+                    {
+                        if (first)
+                        {
+                            first = false;
+                            doc.Root.Add(new XComment($" Package: {id}"));
+
+                            var propertyGroup = new XElement("PropertyGroup",
+                                new XAttribute("Condition", $" '$(PackageId)' == '{id}' "),
+                                new XElement("BaselinePackageVersion", version));
+                            doc.Root.Add(propertyGroup);
+                        }
+
+                        var itemGroup = new XElement("ItemGroup", new XAttribute("Condition", $" '$(PackageId)' == '{id}' AND '$(TargetFramework)' == '{group.TargetFramework.GetShortFolderName()}' "));
+                        doc.Root.Add(itemGroup);
+
+                        foreach (var dependency in group.Packages)
+                        {
+                            itemGroup.Add(new XElement("BaselinePackageReference", new XAttribute("Include", dependency.Id), new XAttribute("Version", dependency.VersionRange.ToString())));
+                        }
+                    }
+                }
+            }
+
+            var settings = new XmlWriterSettings
+            {
+                OmitXmlDeclaration = true,
+                Encoding = Encoding.UTF8,
+                Indent = true,
+            };
+            using (var writer = XmlWriter.Create(output, settings))
+            {
+                doc.Save(writer);
+            }
+
+            return 0;
+        }
+    }
+}

+ 10 - 0
eng/tools/BaselineGenerator/README.md

@@ -0,0 +1,10 @@
+BaselineGenerator
+=================
+
+This tool is used to generate an MSBuild file which sets the "baseline" against which servicing updates are built.
+
+## Usage
+
+1. Add to the [baseline.xml](./baseline.xml) a list of package ID's and their latest released versions. The source of this information can typically
+  be found in the build.xml file generated during ProdCon builds. See https://github.com/dotnet/versions/blob/master/build-info/dotnet/product/cli/release/2.1.6/build.xml for example.
+2. Run `dotnet run` on this project.

+ 15 - 0
eng/tools/BaselineGenerator/baseline.xml

@@ -0,0 +1,15 @@
+<Baseline Version="2.2.0">
+  <Package Id="Microsoft.AspNetCore.Cryptography.Internal" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.Abstractions" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.AzureKeyVault" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.Extensions" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.DataProtection.SystemWeb" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.Html.Abstractions" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.JsonPatch" Version="2.2.0" />
+  <Package Id="Microsoft.AspNetCore.WebSockets" Version="2.2.0" />
+</Baseline>

+ 3 - 0
eng/tools/Directory.Build.props

@@ -0,0 +1,3 @@
+<Project>
+  <Import Project="..\..\build\sources.props" />
+</Project>

+ 2 - 0
eng/tools/Directory.Build.targets

@@ -0,0 +1,2 @@
+<Project>
+</Project>

+ 34 - 0
eng/tools/tools.sln

@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaselineGenerator", "BaselineGenerator\BaselineGenerator.csproj", "{CF76A947-3A72-4824-87E6-BF029D84218B}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x64.Build.0 = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x86.Build.0 = Debug|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x64.ActiveCfg = Release|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x64.Build.0 = Release|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x86.ActiveCfg = Release|Any CPU
+		{CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x86.Build.0 = Release|Any CPU
+	EndGlobalSection
+EndGlobal

+ 1 - 1
global.json

@@ -3,6 +3,6 @@
         "version": "2.2.100-rtm-009578"
     },
     "msbuild-sdks": {
-        "Internal.AspNetCore.Sdk": "2.2.0-preview2-20181109.5"
+        "Internal.AspNetCore.Sdk": "2.2.1-build-20181114.3"
     }
 }

+ 2 - 2
korebuild-lock.txt

@@ -1,2 +1,2 @@
-version:2.2.0-preview2-20181109.5
-commithash:f736f491e6a372b52791e094f8b93e176c2f98c2
+version:2.2.1-build-20181114.3
+commithash:07ca548548a53e0226eef75eab29a67b0ed7c342

+ 0 - 1
modules/HtmlAbstractions

@@ -1 +0,0 @@
-Subproject commit 6c5ca90d81f9013a8652da4c1752bd0b4d18e908

+ 0 - 1
modules/JsonPatch

@@ -1 +0,0 @@
-Subproject commit 3b485909ebec98d9437058dbcf45dac3ccd1f157

+ 0 - 1
scripts/UpdateDependencies.ps1

@@ -56,7 +56,6 @@ foreach ($package in $remoteDeps.SelectNodes('//Package')) {
 
 if (-not $NoCommit) {
     $currentBranch = Invoke-Block { & git rev-parse --abbrev-ref HEAD }
-
     $destinationBranch = "dotnetbot/UpdateDeps"
     Invoke-Block { & git checkout -tb $destinationBranch "origin/$GithubUpstreamBranch" }
 }

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

@@ -15,7 +15,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector</Description>
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
+    <Reference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj

@@ -5,8 +5,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
   </ItemGroup>
 
 </Project>

+ 3 - 6
src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj

@@ -9,12 +9,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryPackageVersion)" />
-    <PackageReference Include="Microsoft.Azure.KeyVault" Version="$(MicrosoftAzureKeyVaultPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.Azure.KeyVault" />
+    <Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" />
   </ItemGroup>
 
 </Project>

+ 3 - 6
src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj

@@ -6,12 +6,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
   </ItemGroup>
 
 </Project>

+ 2 - 5
src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj

@@ -9,11 +9,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStoragePackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="WindowsAzure.Storage" />
   </ItemGroup>
 
 </Project>

+ 3 - 6
src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj

@@ -6,12 +6,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj

@@ -10,7 +10,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj

@@ -9,7 +9,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj

@@ -10,8 +10,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" />
   </ItemGroup>
 
 </Project>

+ 9 - 12
src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj

@@ -14,18 +14,15 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
-    <ProjectReference Include="..\..\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
-    <PackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
-    <PackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
-    <PackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
+    <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
+    <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
+    <Reference Include="Microsoft.Extensions.Options" />
+    <Reference Include="Microsoft.Win32.Registry" />
+    <Reference Include="System.Security.Cryptography.Xml" />
+    <Reference Include="System.Security.Principal.Windows" />
   </ItemGroup>
 
 </Project>

+ 5 - 5
src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj

@@ -11,12 +11,12 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
+    <!-- TODO replace all PackageReference with ProjectReference once aspnet/Hosting has merged to this repo -->
+    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.1" />
 
-  <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
   </ItemGroup>
 
 </Project>

+ 0 - 19
src/DataProtection/Directory.Build.props

@@ -1,19 +0,0 @@
-<Project>
-
-  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
-
-  <Import Project="version.props" />
-
-  <ItemGroup>
-    <PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
-  </ItemGroup>
-
-  <ItemGroup Condition=" '$(IsTestProject)' == 'true' ">
-    <PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
-    <PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
-    <PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
-  </ItemGroup>
-
-</Project>

+ 2 - 9
src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj

@@ -9,15 +9,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(MicrosoftEntityFrameworkCorePackageVersion)" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="Properties\" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.EntityFrameworkCore" />
   </ItemGroup>
 
 </Project>

+ 2 - 5
src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj

@@ -5,11 +5,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" />
+    <Reference Include="Microsoft.EntityFrameworkCore.InMemory" />
   </ItemGroup>
 
 </Project>

+ 2 - 5
src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj

@@ -12,11 +12,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
   </ItemGroup>
 
 </Project>

+ 3 - 2
src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj

@@ -10,8 +10,9 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
+    <Reference Include="Microsoft.AspNetCore.Cryptography.Internal" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
   </ItemGroup>
 
 </Project>

+ 2 - 5
src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj

@@ -9,11 +9,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="StackExchange.Redis" Version="$(StackExchangeRedisPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="StackExchange.Redis" />
   </ItemGroup>
 
 </Project>

+ 5 - 8
src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj

@@ -11,14 +11,11 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
-    <ProjectReference Include="..\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" />
+    <Reference Include="Microsoft.Extensions.Configuration.Json" />
+    <Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
   </ItemGroup>
 
 </Project>

+ 2 - 5
src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj

@@ -13,11 +13,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Web" />
   </ItemGroup>

+ 0 - 12
src/DataProtection/build/repo.props

@@ -1,12 +0,0 @@
-<Project>
-  <Import Project="..\..\..\build\dependencies.props" />
-  <Import Project="..\..\..\build\runtimes.props" />
-  <PropertyGroup>
-    <!-- TODO: temporary while we reorganize source code and refactor dependency management -->
-    <DisablePackageReferenceRestrictions>true</DisablePackageReferenceRestrictions>
-    <OverridePackageOutputPath>false</OverridePackageOutputPath>
-    <BuildSolutions>false</BuildSolutions>
-    <DataProtectionRoot>$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..\'))</DataProtectionRoot>
-    <Projects>$(DataProtectionRoot)**\*.csproj</Projects>
-  </PropertyGroup>
-</Project>

+ 5 - 8
src/DataProtection/samples/AzureBlob/AzureBlob.csproj

@@ -6,14 +6,11 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-    <ProjectReference Include="..\..\AzureStorage\src\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
+    <Reference Include="Microsoft.Extensions.Logging" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
   </ItemGroup>
 
 </Project>

+ 6 - 9
src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj

@@ -6,15 +6,12 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\AzureKeyVault\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" />
+    <Reference Include="Microsoft.Extensions.Configuration" />
+    <Reference Include="Microsoft.Extensions.Configuration.Json" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
+    <Reference Include="Microsoft.Extensions.Logging" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
   </ItemGroup>
 
 </Project>

+ 4 - 7
src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj

@@ -6,13 +6,10 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-    <ProjectReference Include="..\..\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
+    <Reference Include="Microsoft.Extensions.Logging" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj

@@ -6,8 +6,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
-    <ProjectReference Include="..\..\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/DataProtection/samples/NonDISample/NonDISample.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
   </ItemGroup>
 
 </Project>

+ 4 - 7
src/DataProtection/samples/Redis/Redis.csproj

@@ -6,13 +6,10 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\StackExchangeRedis\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
+    <Reference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
+    <Reference Include="Microsoft.Extensions.Logging" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
   </ItemGroup>
 
 </Project>

+ 0 - 10
src/DataProtection/version.props

@@ -1,10 +0,0 @@
-<Project>
-  <PropertyGroup>
-    <VersionPrefix>2.2.0</VersionPrefix>
-    <VersionSuffix>rtm</VersionSuffix>
-    <PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
-    <PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
-    <BuildNumber Condition="'$(BuildNumber)' == ''">t000</BuildNumber>
-    <VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
-  </PropertyGroup>
-</Project>

+ 49 - 0
src/Features/JsonPatch/src/Adapters/AdapterFactory.cs

@@ -0,0 +1,49 @@
+using Microsoft.AspNetCore.JsonPatch.Internal;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.AspNetCore.JsonPatch.Adapters
+{
+    /// <summary>
+    /// The default AdapterFactory to be used for resolving <see cref="IAdapter"/>.
+    /// </summary>
+    public class AdapterFactory : IAdapterFactory
+    {
+        /// <inheritdoc />
+        public virtual IAdapter Create(object target, IContractResolver contractResolver)
+        {
+            if (target == null)
+            {
+                throw new ArgumentNullException(nameof(target));
+            }
+
+            if (contractResolver == null)
+            {
+                throw new ArgumentNullException(nameof(contractResolver));
+            }
+
+            var jsonContract = contractResolver.ResolveContract(target.GetType());
+
+            if (target is IList)
+            {
+                return new ListAdapter();
+            }
+            else if (jsonContract is JsonDictionaryContract jsonDictionaryContract)
+            {
+                var type = typeof(DictionaryAdapter<,>).MakeGenericType(jsonDictionaryContract.DictionaryKeyType, jsonDictionaryContract.DictionaryValueType);
+                return (IAdapter)Activator.CreateInstance(type);
+            }
+            else if (jsonContract is JsonDynamicContract)
+            {
+                return new DynamicObjectAdapter();
+            }
+            else
+            {
+                return new PocoAdapter();
+            }
+        }
+    }
+}

+ 22 - 0
src/Features/JsonPatch/src/Adapters/IAdapterFactory.cs

@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.JsonPatch.Internal;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.AspNetCore.JsonPatch.Adapters
+{
+    /// <summary>
+    /// Defines the operations used for loading an <see cref="IAdapter"/> based on the current object and ContractResolver.
+    /// </summary>
+    public interface IAdapterFactory
+    {
+        /// <summary>
+        /// Creates an <see cref="IAdapter"/> for the current object 
+        /// </summary>
+        /// <param name="target">The target object</param>
+        /// <param name="contractResolver">The current contract resolver</param>
+        /// <returns>The needed <see cref="IAdapter"/></returns>
+        IAdapter Create(object target, IContractResolver contractResolver);
+    }
+}

+ 112 - 0
src/Features/JsonPatch/src/Adapters/IObjectAdapter.cs

@@ -0,0 +1,112 @@
+// 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.AspNetCore.JsonPatch.Operations;
+
+namespace Microsoft.AspNetCore.JsonPatch.Adapters
+{
+    /// <summary>
+    /// Defines the operations that can be performed on a JSON patch document.
+    /// </summary>  
+    public interface IObjectAdapter
+    {
+        /// <summary>
+        /// Using the "add" operation a new value is inserted into the root of the target
+        /// document, into the target array at the specified valid index, or to a target object at
+        /// the specified location.
+        /// 
+        /// When adding to arrays, the specified index MUST NOT be greater than the number of elements in the array.  
+        /// To append the value to the array, the index of "-" character is used (see [RFC6901]).
+        /// 
+        /// When adding to an object, if an object member does not already exist, a new member is added to the object at the 
+        /// specified location or if an object member does exist, that member's value is replaced.
+        ///
+        /// The operation object MUST contain a "value" member whose content
+        /// specifies the value to be added.
+        ///
+        /// For example:
+        ///
+        /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }
+        ///
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-4
+        /// </summary>
+        /// <param name="operation">The add operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Add(Operation operation, object objectToApplyTo);
+
+        /// <summary>
+        ///  Using the "copy" operation, a value is copied from a specified location to the
+        ///  target location.
+        ///
+        ///  The operation object MUST contain a "from" member, which references the location in the
+        ///  target document to copy the value from.
+        ///
+        ///  The "from" location MUST exist for the operation to be successful.
+        ///
+        ///  For example:
+        ///
+        ///  { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
+        ///  
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7
+        /// </summary>
+        /// <param name="operation">The copy operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Copy(Operation operation, object objectToApplyTo);
+
+        /// <summary>
+        /// Using the "move" operation the value at a specified location is removed and
+        /// added to the target location.
+        ///
+        /// The operation object MUST contain a "from" member, which references the location in the
+        /// target document to move the value from.
+        ///
+        /// The "from" location MUST exist for the operation to be successful.
+        ///
+        /// For example:
+        ///
+        /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
+        ///
+        /// A location cannot be moved into one of its children.
+        /// 
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
+        /// </summary>
+        /// <param name="operation">The move operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Move(Operation operation, object objectToApplyTo);
+
+        /// <summary>
+        /// Using the "remove" operation the value at the target location is removed.
+        ///
+        /// The target location MUST exist for the operation to be successful.
+        ///
+        /// For example:
+        ///
+        /// { "op": "remove", "path": "/a/b/c" }
+        ///
+        /// If removing an element from an array, any elements above the
+        /// specified index are shifted one position to the left.
+        /// 
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
+        /// </summary>
+        /// <param name="operation">The remove operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Remove(Operation operation, object objectToApplyTo);
+
+        /// <summary>
+        /// Using the "replace" operation he value at the target location is replaced
+        /// with a new value.  The operation object MUST contain a "value" member
+        /// which specifies the replacement value.
+        ///
+        /// The target location MUST exist for the operation to be successful.
+        ///
+        /// For example:
+        ///
+        /// { "op": "replace", "path": "/a/b/c", "value": 42 }
+        /// 
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6
+        /// </summary>
+        /// <param name="operation">The replace operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Replace(Operation operation, object objectToApplyTo);
+    }
+}

+ 32 - 0
src/Features/JsonPatch/src/Adapters/IObjectAdapterWithTest.cs

@@ -0,0 +1,32 @@
+// 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.AspNetCore.JsonPatch.Operations;
+
+namespace Microsoft.AspNetCore.JsonPatch.Adapters
+{
+    /// <summary>
+    /// Defines the operations that can be performed on a JSON patch document, including "test".
+    /// </summary>
+    public interface IObjectAdapterWithTest : IObjectAdapter
+    {
+        /// <summary>
+        /// Using the "test" operation a value at the target location is compared for
+        /// equality to a specified value.
+        /// 
+        /// The operation object MUST contain a "value" member that specifies 
+        /// value to be compared to the target location's value.
+        /// 
+        /// The target location MUST be equal to the "value" value for the 
+        /// operation to be considered successful.
+        /// 
+        /// For example:
+        /// { "op": "test", "path": "/a/b/c", "value": "foo" }
+        /// 
+        /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7
+        /// </summary>
+        /// <param name="operation">The test operation.</param>
+        /// <param name="objectToApplyTo">Object to apply the operation to.</param>
+        void Test(Operation operation, object objectToApplyTo);
+    }
+}

+ 348 - 0
src/Features/JsonPatch/src/Adapters/ObjectAdapter.cs

@@ -0,0 +1,348 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Internal;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Adapters
+{
+    /// <inheritdoc />
+    public class ObjectAdapter : IObjectAdapterWithTest
+    {
+        /// <summary>
+        /// Initializes a new instance of <see cref="ObjectAdapter"/>.
+        /// </summary>
+        /// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
+        /// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError"/>.</param>
+        public ObjectAdapter(
+            IContractResolver contractResolver,
+            Action<JsonPatchError> logErrorAction):
+            this(contractResolver, logErrorAction, new AdapterFactory())
+        {
+        }
+
+        /// <summary>
+         /// Initializes a new instance of <see cref="ObjectAdapter"/>.
+         /// </summary>
+         /// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
+         /// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError"/>.</param>
+        /// <param name="adapterFactory">The <see cref="IAdapterFactory"/> to use when creating adaptors.</param>
+         public ObjectAdapter(
+            IContractResolver contractResolver,
+            Action<JsonPatchError> logErrorAction,
+            IAdapterFactory adapterFactory)
+         {
+             ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
+            LogErrorAction = logErrorAction;
+            AdapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory));
+         }
+
+        /// <summary>
+        /// Gets or sets the <see cref="IContractResolver"/>.
+        /// </summary>
+        public IContractResolver ContractResolver { get; }
+
+        /// <summary>
+        /// Gets or sets the <see cref="IAdapterFactory"/>
+        /// </summary>
+        public IAdapterFactory AdapterFactory { get; }
+
+        /// <summary>
+        /// Action for logging <see cref="JsonPatchError"/>.
+        /// </summary>
+        public Action<JsonPatchError> LogErrorAction { get; }
+
+        public void Add(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            Add(operation.path, operation.value, objectToApplyTo, operation);
+        }
+
+        /// <summary>
+        /// Add is used by various operations (eg: add, copy, ...), yet through different operations;
+        /// This method allows code reuse yet reporting the correct operation on error
+        /// </summary>
+        private void Add(
+            string path,
+            object value,
+            object objectToApplyTo,
+            Operation operation)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            var parsedPath = new ParsedPath(path);
+            var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
+
+            var target = objectToApplyTo;
+            if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
+            {
+                var error = CreatePathNotFoundError(objectToApplyTo, path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+
+            if (!adapter.TryAdd(target, parsedPath.LastSegment, ContractResolver, value, out errorMessage))
+            {
+                var error = CreateOperationFailedError(objectToApplyTo, path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+        }
+
+        public void Move(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            // Get value at 'from' location and add that value to the 'path' location
+            if (TryGetValue(operation.from, objectToApplyTo, operation, out var propertyValue))
+            {
+                // remove that value
+                Remove(operation.from, objectToApplyTo, operation);
+
+                // add that value to the path location
+                Add(operation.path,
+                    propertyValue,
+                    objectToApplyTo,
+                    operation);
+            }
+        }
+
+        public void Remove(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            Remove(operation.path, objectToApplyTo, operation);
+        }
+
+        /// <summary>
+        /// Remove is used by various operations (eg: remove, move, ...), yet through different operations;
+        /// This method allows code reuse yet reporting the correct operation on error.  The return value
+        /// contains the type of the item that has been removed (and a bool possibly signifying an error)
+        /// This can be used by other methods, like replace, to ensure that we can pass in the correctly
+        /// typed value to whatever method follows.
+        /// </summary>
+        private void Remove(string path, object objectToApplyTo, Operation operationToReport)
+        {
+            var parsedPath = new ParsedPath(path);
+            var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
+
+            var target = objectToApplyTo;
+            if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
+            {
+                var error = CreatePathNotFoundError(objectToApplyTo, path, operationToReport, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+
+            if (!adapter.TryRemove(target, parsedPath.LastSegment, ContractResolver, out errorMessage))
+            {
+                var error = CreateOperationFailedError(objectToApplyTo, path, operationToReport, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+        }
+
+        public void Replace(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            var parsedPath = new ParsedPath(operation.path);
+            var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
+
+            var target = objectToApplyTo;
+            if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
+            {
+                var error = CreatePathNotFoundError(objectToApplyTo, operation.path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+
+            if (!adapter.TryReplace(target, parsedPath.LastSegment, ContractResolver, operation.value, out errorMessage))
+            {
+                var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+        }
+
+        public void Copy(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            // Get value at 'from' location and add that value to the 'path' location
+            if (TryGetValue(operation.from, objectToApplyTo, operation, out var propertyValue))
+            {
+                // Create deep copy
+                var copyResult = ConversionResultProvider.CopyTo(propertyValue, propertyValue.GetType());
+                if (copyResult.CanBeConverted)
+                {
+                    Add(operation.path,
+                        copyResult.ConvertedInstance,
+                        objectToApplyTo,
+                        operation);
+                }
+                else
+                {
+                    var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, Resources.FormatCannotCopyProperty(operation.from));
+                    ErrorReporter(error);
+                    return;
+                }
+            }
+        }
+
+        public void Test(Operation operation, object objectToApplyTo)
+        {
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            var parsedPath = new ParsedPath(operation.path);
+            var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
+
+            var target = objectToApplyTo;
+            if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
+            {
+                var error = CreatePathNotFoundError(objectToApplyTo, operation.path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+
+            if (!adapter.TryTest(target, parsedPath.LastSegment, ContractResolver, operation.value, out errorMessage))
+            {
+                var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, errorMessage);
+                ErrorReporter(error);
+                return;
+            }
+        }
+
+        private bool TryGetValue(
+            string fromLocation,
+            object objectToGetValueFrom,
+            Operation operation,
+            out object propertyValue)
+        {
+            if (fromLocation == null)
+            {
+                throw new ArgumentNullException(nameof(fromLocation));
+            }
+
+            if (objectToGetValueFrom == null)
+            {
+                throw new ArgumentNullException(nameof(objectToGetValueFrom));
+            }
+
+            if (operation == null)
+            {
+                throw new ArgumentNullException(nameof(operation));
+            }
+
+            propertyValue = null;
+
+            var parsedPath = new ParsedPath(fromLocation);
+            var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
+
+            var target = objectToGetValueFrom;
+            if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
+            {
+                var error = CreatePathNotFoundError(objectToGetValueFrom, fromLocation, operation, errorMessage);
+                ErrorReporter(error);
+                return false;
+            }
+
+            if (!adapter.TryGet(target, parsedPath.LastSegment, ContractResolver, out propertyValue, out errorMessage))
+            {
+                var error = CreateOperationFailedError(objectToGetValueFrom, fromLocation, operation, errorMessage);
+                ErrorReporter(error);
+                return false;
+            }
+
+            return true;
+        }
+
+        private Action<JsonPatchError> ErrorReporter
+        {
+            get
+            {
+                return LogErrorAction ?? Internal.ErrorReporter.Default;
+            }
+        }
+
+        private JsonPatchError CreateOperationFailedError(object target, string path, Operation operation, string errorMessage)
+        {
+            return new JsonPatchError(
+                target,
+                operation,
+                errorMessage ?? Resources.FormatCannotPerformOperation(operation.op, path));
+        }
+
+        private JsonPatchError CreatePathNotFoundError(object target, string path, Operation operation, string errorMessage)
+        {
+            return new JsonPatchError(
+                target,
+                operation,
+                errorMessage ?? Resources.FormatTargetLocationNotFound(operation.op, path));
+        }
+    }
+}

+ 76 - 0
src/Features/JsonPatch/src/Converters/JsonPatchDocumentConverter.cs

@@ -0,0 +1,76 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Exceptions;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Converters
+{
+    public class JsonPatchDocumentConverter : JsonConverter
+    {
+        public override bool CanConvert(Type objectType)
+        {
+            return true;
+        }
+
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 
+            JsonSerializer serializer)
+        {
+            if (objectType != typeof(JsonPatchDocument))
+            {
+                throw new ArgumentException(Resources.FormatParameterMustMatchType("objectType", "JsonPatchDocument"), "objectType");
+            }
+
+            try
+            {
+                if (reader.TokenType == JsonToken.Null)
+                {
+                    return null;
+                }
+                
+                // load jObject
+                var jObject = JArray.Load(reader);
+
+                // Create target object for Json => list of operations
+                var targetOperations = new List<Operation>();
+
+                // Create a new reader for this jObject, and set all properties 
+                // to match the original reader.
+                var jObjectReader = jObject.CreateReader();
+                jObjectReader.Culture = reader.Culture;
+                jObjectReader.DateParseHandling = reader.DateParseHandling;
+                jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
+                jObjectReader.FloatParseHandling = reader.FloatParseHandling;
+
+                // Populate the object properties
+                serializer.Populate(jObjectReader, targetOperations);
+
+                // container target: the JsonPatchDocument. 
+                var container = new JsonPatchDocument(targetOperations, new DefaultContractResolver());
+
+                return container;
+            }
+            catch (Exception ex)
+            {
+                throw new JsonSerializationException(Resources.InvalidJsonPatchDocument, ex);
+            }
+        }
+
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            if (value is IJsonPatchDocument)
+            {
+                var jsonPatchDoc = (IJsonPatchDocument)value;
+                var lst = jsonPatchDoc.GetOperations();
+
+                // write out the operations, no envelope
+                serializer.Serialize(writer, lst);
+            }
+        }
+    }
+}

+ 64 - 0
src/Features/JsonPatch/src/Converters/TypedJsonPatchDocumentConverter.cs

@@ -0,0 +1,64 @@
+// 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.Reflection;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Converters
+{
+    public class TypedJsonPatchDocumentConverter : JsonPatchDocumentConverter
+    {
+        public override object ReadJson(
+            JsonReader reader,
+            Type objectType,
+            object existingValue,
+            JsonSerializer serializer)
+        {
+            try
+            {
+                if (reader.TokenType == JsonToken.Null)
+                {
+                    return null;
+                }
+
+                var genericType = objectType.GetTypeInfo().GenericTypeArguments[0];
+
+                // load jObject
+                var jObject = JArray.Load(reader);
+
+                // Create target object for Json => list of operations, typed to genericType
+                var genericOperation = typeof(Operation<>);
+                var concreteOperationType = genericOperation.MakeGenericType(genericType);
+
+                var genericList = typeof(List<>);
+                var concreteList = genericList.MakeGenericType(concreteOperationType);
+
+                var targetOperations = Activator.CreateInstance(concreteList);
+
+                //Create a new reader for this jObject, and set all properties to match the original reader.
+                var jObjectReader = jObject.CreateReader();
+                jObjectReader.Culture = reader.Culture;
+                jObjectReader.DateParseHandling = reader.DateParseHandling;
+                jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
+                jObjectReader.FloatParseHandling = reader.FloatParseHandling;
+
+                // Populate the object properties
+                serializer.Populate(jObjectReader, targetOperations);
+
+                // container target: the typed JsonPatchDocument.
+                var container = Activator.CreateInstance(objectType, targetOperations, new DefaultContractResolver());
+
+                return container;
+            }
+            catch (Exception ex)
+            {
+                throw new JsonSerializationException(Resources.InvalidJsonPatchDocument, ex);
+            }
+        }
+    }
+}

+ 38 - 0
src/Features/JsonPatch/src/Exceptions/JsonPatchException.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;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+
+namespace Microsoft.AspNetCore.JsonPatch.Exceptions
+{
+    public class JsonPatchException : Exception 
+    {
+        public Operation FailedOperation { get; private set; }
+        public object AffectedObject { get; private set; }
+ 
+
+        public JsonPatchException()
+        {
+
+        }
+
+        public JsonPatchException(JsonPatchError jsonPatchError, Exception innerException)
+            : base(jsonPatchError.ErrorMessage, innerException)
+        {
+            FailedOperation = jsonPatchError.Operation;
+            AffectedObject = jsonPatchError.AffectedObject;
+        }
+
+        public JsonPatchException(JsonPatchError jsonPatchError)
+          : this(jsonPatchError, null)          
+        {
+        } 
+
+        public JsonPatchException(string message, Exception innerException)
+            : base (message, innerException)
+        {
+           
+        }
+    }
+}

+ 30 - 0
src/Features/JsonPatch/src/Helpers/GetValueResult.cs

@@ -0,0 +1,30 @@
+// 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.JsonPatch.Helpers
+{
+    /// <summary>
+    /// Return value for the helper method used by Copy/Move.  Needed to ensure we can make a different
+    /// decision in the calling method when the value is null because it cannot be fetched (HasError = true) 
+    /// versus when it actually is null (much like why RemovedPropertyTypeResult is used for returning 
+    /// type in the Remove operation).
+    /// </summary>
+    public class GetValueResult
+    {
+        public GetValueResult(object propertyValue, bool hasError)
+        {
+            PropertyValue = propertyValue;
+            HasError = hasError;
+        }
+
+        /// <summary>
+        /// The value of the property we're trying to get
+        /// </summary>
+        public object PropertyValue { get; private set; }
+
+        /// <summary>
+        /// HasError: true when an error occurred, the operation didn't complete succesfully
+        /// </summary>
+        public bool HasError { get; private set; }
+    }
+}

+ 43 - 0
src/Features/JsonPatch/src/Helpers/JsonPatchProperty.cs

@@ -0,0 +1,43 @@
+// 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 Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    /// <summary>
+    /// Metadata for JsonProperty.
+    /// </summary>
+    public class JsonPatchProperty
+    {
+        /// <summary>
+        /// Initializes a new instance.
+        /// </summary>
+        public JsonPatchProperty(JsonProperty property, object parent)
+        {
+            if (property == null)
+            {
+                throw new ArgumentNullException(nameof(property));
+            }
+
+            if (parent == null)
+            {
+                throw new ArgumentNullException(nameof(parent));
+            }
+
+            Property = property;
+            Parent = parent;
+        }
+
+        /// <summary>
+        /// Gets or sets JsonProperty.
+        /// </summary>
+        public JsonProperty Property { get; set; }
+
+        /// <summary>
+        /// Gets or sets Parent.
+        /// </summary>
+        public object Parent { get; set; }
+    }
+}

+ 16 - 0
src/Features/JsonPatch/src/IJsonPatchDocument.cs

@@ -0,0 +1,16 @@
+// 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.AspNetCore.JsonPatch.Operations;
+using System.Collections.Generic;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    public interface IJsonPatchDocument
+    {
+        IContractResolver ContractResolver { get; set; }
+
+        IList<Operation> GetOperations();
+    }
+}

+ 17 - 0
src/Features/JsonPatch/src/Internal/ConversionResult.cs

@@ -0,0 +1,17 @@
+// 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.JsonPatch.Internal
+{
+    public class ConversionResult
+    {
+        public ConversionResult(bool canBeConverted, object convertedInstance)
+        {
+            CanBeConverted = canBeConverted;
+            ConvertedInstance = convertedInstance;
+        }
+
+        public bool CanBeConverted { get; }
+        public object ConvertedInstance { get; }
+    }
+}

+ 75 - 0
src/Features/JsonPatch/src/Internal/ConversionResultProvider.cs

@@ -0,0 +1,75 @@
+// 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 Newtonsoft.Json;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public static class ConversionResultProvider
+    {
+        public static ConversionResult ConvertTo(object value, Type typeToConvertTo)
+        {
+            if (value == null)
+            {
+                return new ConversionResult(IsNullableType(typeToConvertTo), null);
+            }
+            else if (typeToConvertTo.IsAssignableFrom(value.GetType()))
+            {
+                // No need to convert
+                return new ConversionResult(true, value);
+            }
+            else
+            {
+                try
+                {
+                    var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), typeToConvertTo);
+                    return new ConversionResult(true, deserialized);
+                }
+                catch
+                {
+                    return new ConversionResult(canBeConverted: false, convertedInstance: null);
+                }
+            }
+        }
+
+        public static ConversionResult CopyTo(object value, Type typeToConvertTo)
+        {
+            var targetType = typeToConvertTo;
+            if (value == null)
+            {
+                return new ConversionResult(IsNullableType(typeToConvertTo), null);
+            }
+            else if (typeToConvertTo.IsAssignableFrom(value.GetType()))
+            {
+                // Keep original type
+                targetType = value.GetType();
+            }
+            try
+            {
+                var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), targetType);
+                return new ConversionResult(true, deserialized);
+            }
+            catch
+            {
+                return new ConversionResult(canBeConverted: false, convertedInstance: null);
+            }
+        }
+
+        private static bool IsNullableType(Type type)
+        {
+            var typeInfo = type.GetTypeInfo();
+            if (typeInfo.IsValueType)
+            {
+                // value types are only nullable if they are Nullable<T>
+                return typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
+            }
+            else
+            {
+                // reference types are always nullable
+                return true;
+            }
+        }
+    }
+}

+ 245 - 0
src/Features/JsonPatch/src/Internal/DictionaryAdapterOfTU.cs

@@ -0,0 +1,245 @@
+// 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public class DictionaryAdapter<TKey, TValue> : IAdapter
+    {
+        public virtual bool TryAdd(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            // As per JsonPatch spec, if a key already exists, adding should replace the existing value
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            dictionary[convertedKey] = convertedValue;
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryGet(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                value = null;
+                return false;
+            }
+
+            if (!dictionary.ContainsKey(convertedKey))
+            {
+                value = null;
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            value = dictionary[convertedKey];
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryRemove(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                return false;
+            }
+
+            // As per JsonPatch spec, the target location must exist for remove to be successful
+            if (!dictionary.ContainsKey(convertedKey))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            dictionary.Remove(convertedKey);
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryReplace(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                return false;
+            }
+
+            // As per JsonPatch spec, the target location must exist for remove to be successful
+            if (!dictionary.ContainsKey(convertedKey))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!TryConvertValue(value, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            dictionary[convertedKey] = convertedValue;
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryTest(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                return false;
+            }
+
+            // As per JsonPatch spec, the target location must exist for test to be successful
+            if (!dictionary.ContainsKey(convertedKey))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!TryConvertValue(value, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            var currentValue = dictionary[convertedKey];
+
+            // The target segment does not have an assigned value to compare the test value with
+            if (currentValue == null || string.IsNullOrEmpty(currentValue.ToString()))
+            {
+                errorMessage = Resources.FormatValueForTargetSegmentCannotBeNullOrEmpty(segment);
+                return false;
+            }
+
+            if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue)))
+            {
+                errorMessage = Resources.FormatValueNotEqualToTestValue(currentValue, value, segment);
+                return false;
+            }
+            else
+            {
+                errorMessage = null;
+                return true;
+            }
+        }
+
+        public virtual bool TryTraverse(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object nextTarget,
+            out string errorMessage)
+        {
+            var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
+            var key = contract.DictionaryKeyResolver(segment);
+            var dictionary = (IDictionary<TKey, TValue>)target;
+
+            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
+            {
+                nextTarget = null;
+                return false;
+            }
+
+            if (dictionary.ContainsKey(convertedKey))
+            {
+                nextTarget = dictionary[convertedKey];
+                errorMessage = null;
+                return true;
+            }
+            else
+            {
+                nextTarget = null;
+                errorMessage = null;
+                return false;
+            }
+        }
+
+        protected virtual bool TryConvertKey(string key, out TKey convertedKey, out string errorMessage)
+        {
+            var conversionResult = ConversionResultProvider.ConvertTo(key, typeof(TKey));
+            if (conversionResult.CanBeConverted)
+            {
+                errorMessage = null;
+                convertedKey = (TKey)conversionResult.ConvertedInstance;
+                return true;
+            }
+            else
+            {
+                errorMessage = Resources.FormatInvalidPathSegment(key);
+                convertedKey = default(TKey);
+                return false;
+            }
+        }
+
+        protected virtual bool TryConvertValue(object value, out TValue convertedValue, out string errorMessage)
+        {
+            var conversionResult = ConversionResultProvider.ConvertTo(value, typeof(TValue));
+            if (conversionResult.CanBeConverted)
+            {
+                errorMessage = null;
+                convertedValue = (TValue)conversionResult.ConvertedInstance;
+                return true;
+            }
+            else
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                convertedValue = default(TValue);
+                return false;
+            }
+        }
+    }
+}

+ 243 - 0
src/Features/JsonPatch/src/Internal/DynamicObjectAdapter.cs

@@ -0,0 +1,243 @@
+// 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.Reflection;
+using System.Runtime.CompilerServices;
+using Microsoft.CSharp.RuntimeBinder;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+using CSharpBinder = Microsoft.CSharp.RuntimeBinder;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public class DynamicObjectAdapter : IAdapter
+    {
+        public virtual bool TryAdd(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TrySetDynamicObjectProperty(target, contractResolver, segment, value, out errorMessage))
+            {
+                return false;
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryGet(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out value, out errorMessage))
+            {
+                value = null;
+                return false;
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryRemove(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out string errorMessage)
+        {
+            if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage))
+            {
+                return false;
+            }
+
+            // Setting the value to "null" will use the default value in case of value types, and
+            // null in case of reference types
+            object value = null;
+            if (property.GetType().GetTypeInfo().IsValueType
+                && Nullable.GetUnderlyingType(property.GetType()) == null)
+            {
+                value = Activator.CreateInstance(property.GetType());
+            }
+
+            if (!TrySetDynamicObjectProperty(target, contractResolver, segment, value, out errorMessage))
+            {
+                return false;
+            }
+
+            errorMessage = null;
+            return true;
+
+        }
+
+        public virtual bool TryReplace(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, property.GetType(), out var convertedValue))
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                return false;
+            }
+
+            if (!TrySetDynamicObjectProperty(target, contractResolver, segment, convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryTest(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, property.GetType(), out var convertedValue))
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                return false;
+            }
+
+            if (!JToken.DeepEquals(JsonConvert.SerializeObject(property), JsonConvert.SerializeObject(convertedValue)))
+            {
+                errorMessage = Resources.FormatValueNotEqualToTestValue(property, value, segment);
+                return false;
+            }
+            else
+            {
+                errorMessage = null;
+                return true;
+            }
+        }
+
+        public virtual bool TryTraverse(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object nextTarget,
+            out string errorMessage)
+        {
+            if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage))
+            {
+                nextTarget = null;
+                return false;
+            }
+            else
+            {
+                nextTarget = property;
+                errorMessage = null;
+                return true;
+            }
+        }
+
+        protected virtual bool TryGetDynamicObjectProperty(
+            object target,
+            IContractResolver contractResolver,
+            string segment,
+            out object value,
+            out string errorMessage)
+        {
+            var jsonDynamicContract = (JsonDynamicContract)contractResolver.ResolveContract(target.GetType());
+
+            var propertyName = jsonDynamicContract.PropertyNameResolver(segment);
+
+            var binder = CSharpBinder.Binder.GetMember(
+                CSharpBinderFlags.None,
+                propertyName,
+                target.GetType(),
+                new List<CSharpArgumentInfo>
+                {
+                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                });
+
+            var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);
+
+            try
+            {
+                value = callsite.Target(callsite, target);
+                errorMessage = null;
+                return true;
+            }
+            catch (RuntimeBinderException)
+            {
+                value = null;
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+        }
+
+        protected virtual bool TrySetDynamicObjectProperty(
+            object target,
+            IContractResolver contractResolver,
+            string segment,
+            object value,
+            out string errorMessage)
+        {
+            var jsonDynamicContract = (JsonDynamicContract)contractResolver.ResolveContract(target.GetType());
+
+            var propertyName = jsonDynamicContract.PropertyNameResolver(segment);
+
+            var binder = CSharpBinder.Binder.SetMember(
+                CSharpBinderFlags.None,
+                propertyName,
+                target.GetType(),
+                new List<CSharpArgumentInfo>
+                {
+                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                });
+
+            var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);
+
+            try
+            {
+                callsite.Target(callsite, target, value);
+                errorMessage = null;
+                return true;
+            }
+            catch (RuntimeBinderException)
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+        }
+
+        protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue)
+        {
+            var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType);
+            if (!conversionResult.CanBeConverted)
+            {
+                convertedValue = null;
+                return false;
+            }
+
+            convertedValue = conversionResult.ConvertedInstance;
+            return true;
+        }
+    }
+}

+ 16 - 0
src/Features/JsonPatch/src/Internal/ErrorReporter.cs

@@ -0,0 +1,16 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Exceptions;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    internal static class ErrorReporter
+    {
+        public static readonly Action<JsonPatchError> Default = (error) =>
+        {
+            throw new JsonPatchException(error);
+        };
+    }
+}

+ 51 - 0
src/Features/JsonPatch/src/Internal/IAdapter.cs

@@ -0,0 +1,51 @@
+// 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 Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public interface IAdapter
+    {
+        bool TryTraverse(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object nextTarget,
+            out string errorMessage);
+
+        bool TryAdd(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage);
+
+        bool TryRemove(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out string errorMessage);
+
+        bool TryGet(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage);
+
+        bool TryReplace(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage);
+
+        bool TryTest(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage);
+    }
+}

+ 349 - 0
src/Features/JsonPatch/src/Internal/ListAdapter.cs

@@ -0,0 +1,349 @@
+// 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 Microsoft.Extensions.Internal;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public class ListAdapter : IAdapter
+    {
+        public virtual bool TryAdd(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var list = (IList)target;
+
+            if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryGetPositionInfo(list, segment, OperationType.Add, out var positionInfo, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            if (positionInfo.Type == PositionType.EndOfList)
+            {
+                list.Add(convertedValue);
+            }
+            else
+            {
+                list.Insert(positionInfo.Index, convertedValue);
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryGet(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            var list = (IList)target;
+
+            if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage))
+            {
+                value = null;
+                return false;
+            }
+
+            if (!TryGetPositionInfo(list, segment, OperationType.Get, out var positionInfo, out errorMessage))
+            {
+                value = null;
+                return false;
+            }
+
+            if (positionInfo.Type == PositionType.EndOfList)
+            {
+                value = list[list.Count - 1];
+            }
+            else
+            {
+                value = list[positionInfo.Index];
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryRemove(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out string errorMessage)
+        {
+            var list = (IList)target;
+
+            if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryGetPositionInfo(list, segment, OperationType.Remove, out var positionInfo, out errorMessage))
+            {
+                return false;
+            }
+
+            if (positionInfo.Type == PositionType.EndOfList)
+            {
+                list.RemoveAt(list.Count - 1);
+            }
+            else
+            {
+                list.RemoveAt(positionInfo.Index);
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryReplace(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var list = (IList)target;
+
+            if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryGetPositionInfo(list, segment, OperationType.Replace, out var positionInfo, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            if (positionInfo.Type == PositionType.EndOfList)
+            {
+                list[list.Count - 1] = convertedValue;
+            }
+            else
+            {
+                list[positionInfo.Index] = convertedValue;
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryTest(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            var list = (IList)target;
+
+            if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryGetPositionInfo(list, segment, OperationType.Replace, out var positionInfo, out errorMessage))
+            {
+                return false;
+            }
+
+            if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage))
+            {
+                return false;
+            }
+
+            var currentValue = list[positionInfo.Index];
+            if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue)))
+            {
+                errorMessage = Resources.FormatValueAtListPositionNotEqualToTestValue(currentValue, value, positionInfo.Index);
+                return false;
+            }
+            else
+            {
+                errorMessage = null;
+                return true;
+            }
+        }
+
+        public virtual bool TryTraverse(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            var list = target as IList;
+            if (list == null)
+            {
+                value = null;
+                errorMessage = null;
+                return false;
+            }
+
+            var index = -1;
+            if (!int.TryParse(segment, out index))
+            {
+                value = null;
+                errorMessage = Resources.FormatInvalidIndexValue(segment);
+                return false;
+            }
+
+            if (index < 0 || index >= list.Count)
+            {
+                value = null;
+                errorMessage = Resources.FormatIndexOutOfBounds(segment);
+                return false;
+            }
+
+            value = list[index];
+            errorMessage = null;
+            return true;
+        }
+
+        protected virtual bool TryConvertValue(
+            object originalValue,
+            Type listTypeArgument,
+            string segment,
+            out object convertedValue,
+            out string errorMessage)
+        {
+            var conversionResult = ConversionResultProvider.ConvertTo(originalValue, listTypeArgument);
+            if (!conversionResult.CanBeConverted)
+            {
+                convertedValue = null;
+                errorMessage = Resources.FormatInvalidValueForProperty(originalValue);
+                return false;
+            }
+
+            convertedValue = conversionResult.ConvertedInstance;
+            errorMessage = null;
+            return true;
+        }
+
+        protected virtual bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out string errorMessage)
+        {
+            // Arrays are not supported as they have fixed size and operations like Add, Insert do not make sense
+            var listType = list.GetType();
+            if (listType.IsArray)
+            {
+                errorMessage = Resources.FormatPatchNotSupportedForArrays(listType.FullName);
+                listTypeArgument = null;
+                return false;
+            }
+            else
+            {
+                var genericList = ClosedGenericMatcher.ExtractGenericInterface(listType, typeof(IList<>));
+                if (genericList == null)
+                {
+                    errorMessage = Resources.FormatPatchNotSupportedForNonGenericLists(listType.FullName);
+                    listTypeArgument = null;
+                    return false;
+                }
+                else
+                {
+                    listTypeArgument = genericList.GenericTypeArguments[0];
+                    errorMessage = null;
+                    return true;
+                }
+            }
+        }
+
+        protected virtual bool TryGetPositionInfo(
+            IList list,
+            string segment,
+            OperationType operationType,
+            out PositionInfo positionInfo,
+            out string errorMessage)
+        {
+            if (segment == "-")
+            {
+                positionInfo = new PositionInfo(PositionType.EndOfList, -1);
+                errorMessage = null;
+                return true;
+            }
+
+            var position = -1;
+            if (int.TryParse(segment, out position))
+            {
+                if (position >= 0 && position < list.Count)
+                {
+                    positionInfo = new PositionInfo(PositionType.Index, position);
+                    errorMessage = null;
+                    return true;
+                }
+                // As per JSON Patch spec, for Add operation the index value representing the number of elements is valid,
+                // where as for other operations like Remove, Replace, Move and Copy the target index MUST exist.
+                else if (position == list.Count && operationType == OperationType.Add)
+                {
+                    positionInfo = new PositionInfo(PositionType.EndOfList, -1);
+                    errorMessage = null;
+                    return true;
+                }
+                else
+                {
+                    positionInfo = new PositionInfo(PositionType.OutOfBounds, position);
+                    errorMessage = Resources.FormatIndexOutOfBounds(segment);
+                    return false;
+                }
+            }
+            else
+            {
+                positionInfo = new PositionInfo(PositionType.Invalid, -1);
+                errorMessage = Resources.FormatInvalidIndexValue(segment);
+                return false;
+            }
+        }
+
+        protected struct PositionInfo
+        {
+            public PositionInfo(PositionType type, int index)
+            {
+                Type = type;
+                Index = index;
+            }
+
+            public PositionType Type { get; }
+            public int Index { get; }
+        }
+
+        protected enum PositionType
+        {
+            Index, // valid index
+            EndOfList, // '-'
+            Invalid, // Ex: not an integer
+            OutOfBounds
+        }
+
+        protected enum OperationType
+        {
+            Add,
+            Remove,
+            Get,
+            Replace
+        }
+    }
+}

+ 79 - 0
src/Features/JsonPatch/src/Internal/ObjectVisitor.cs

@@ -0,0 +1,79 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Adapters;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public class ObjectVisitor
+    {
+        private readonly IAdapterFactory _adapterFactory;
+        private readonly IContractResolver _contractResolver;
+        private readonly ParsedPath _path;
+
+        /// <summary>
+        /// Initializes a new instance of <see cref="ObjectVisitor"/>.
+        /// </summary>
+        /// <param name="path">The path of the JsonPatch operation</param>
+        /// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
+        public ObjectVisitor(ParsedPath path, IContractResolver contractResolver)
+            :this(path, contractResolver, new AdapterFactory())
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of <see cref="ObjectVisitor"/>.
+        /// </summary>
+        /// <param name="path">The path of the JsonPatch operation</param>
+        /// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
+        /// <param name="adapterFactory">The <see cref="IAdapterFactory"/> to use when creating adaptors.</param>
+        public ObjectVisitor(ParsedPath path, IContractResolver contractResolver, IAdapterFactory adapterFactory)
+        { 
+            _path = path;
+            _contractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
+            _adapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory));
+        }
+
+        public bool TryVisit(ref object target, out IAdapter adapter, out string errorMessage)
+        {
+            if (target == null)
+            {
+                adapter = null;
+                errorMessage = null;
+                return false;
+            }
+
+            adapter = SelectAdapter(target);
+
+            // Traverse until the penultimate segment to get the target object and adapter
+            for (var i = 0; i < _path.Segments.Count - 1; i++)
+            {
+                if (!adapter.TryTraverse(target, _path.Segments[i], _contractResolver, out var next, out errorMessage))
+                {
+                    adapter = null;
+                    return false;
+                }
+                
+                // If we hit a null on an interior segment then we need to stop traversing.
+                if (next == null)
+                {
+                    adapter = null;
+                    return false;
+                }
+
+                target = next;
+                adapter = SelectAdapter(target);
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        private IAdapter SelectAdapter(object targetObject)
+        {
+            return _adapterFactory.Create(targetObject, _contractResolver);
+        }
+    }
+}

+ 92 - 0
src/Features/JsonPatch/src/Internal/ParsedPath.cs

@@ -0,0 +1,92 @@
+// 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.AspNetCore.JsonPatch.Exceptions;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public struct ParsedPath
+    {
+        private static readonly string[] Empty = null;
+
+        private readonly string[] _segments;
+
+        public ParsedPath(string path)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            _segments = ParsePath(path);
+        }
+
+        public string LastSegment
+        {
+            get
+            {
+                if (_segments == null || _segments.Length == 0)
+                {
+                    return null;
+                }
+
+                return _segments[_segments.Length - 1];
+            }
+        }
+
+        public IReadOnlyList<string> Segments => _segments ?? Empty;
+
+        private static string[] ParsePath(string path)
+        {
+            var strings = new List<string>();
+            var sb = new StringBuilder(path.Length);
+
+            for (var i = 0; i < path.Length; i++)
+            {
+                if (path[i] == '/')
+                {
+                    if (sb.Length > 0)
+                    {
+                        strings.Add(sb.ToString());
+                        sb.Length = 0;
+                    }
+                }
+                else if (path[i] == '~')
+                {
+                    ++i;
+                    if (i >= path.Length)
+                    {
+                        throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null);
+                    }
+
+                    if (path[i] == '0')
+                    {
+                        sb.Append('~');
+                    }
+                    else if (path[i] == '1')
+                    {
+                        sb.Append('/');
+                    }
+                    else
+                    {
+                        throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null);
+                    }
+                }
+                else
+                {
+                    sb.Append(path[i]);
+                }
+            }
+
+            if (sb.Length > 0)
+            {
+                strings.Add(sb.ToString());
+            }
+
+            return strings.ToArray();
+        }
+    }
+}

+ 32 - 0
src/Features/JsonPatch/src/Internal/PathHelpers.cs

@@ -0,0 +1,32 @@
+// 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.AspNetCore.JsonPatch.Exceptions;
+using System;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    internal static class PathHelpers
+    {
+        internal static string ValidateAndNormalizePath(string path)
+        {
+            // check for most common path errors on create.  This is not
+            // absolutely necessary, but it allows us to already catch mistakes
+            // on creation of the patch document rather than on execute.
+
+            if (path.Contains("//"))
+            {
+                throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null); 
+            }
+
+            if (!path.StartsWith("/", StringComparison.Ordinal))
+            {
+                return "/" + path;
+            }
+            else
+            {
+                return path;
+            }
+        }
+    }
+}

+ 236 - 0
src/Features/JsonPatch/src/Internal/PocoAdapter.cs

@@ -0,0 +1,236 @@
+// 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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch.Internal
+{
+    public class PocoAdapter : IAdapter
+    {
+        public virtual bool TryAdd(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!jsonProperty.Writable)
+            {
+                errorMessage = Resources.FormatCannotUpdateProperty(segment);
+                return false;
+            }
+
+            if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue))
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                return false;
+            }
+
+            jsonProperty.ValueProvider.SetValue(target, convertedValue);
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryGet(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                value = null;
+                return false;
+            }
+
+            if (!jsonProperty.Readable)
+            {
+                errorMessage = Resources.FormatCannotReadProperty(segment);
+                value = null;
+                return false;
+            }
+
+            value = jsonProperty.ValueProvider.GetValue(target);
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryRemove(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out string errorMessage)
+        {
+            if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!jsonProperty.Writable)
+            {
+                errorMessage = Resources.FormatCannotUpdateProperty(segment);
+                return false;
+            }
+
+            // Setting the value to "null" will use the default value in case of value types, and
+            // null in case of reference types
+            object value = null;
+            if (jsonProperty.PropertyType.GetTypeInfo().IsValueType
+                && Nullable.GetUnderlyingType(jsonProperty.PropertyType) == null)
+            {
+                value = Activator.CreateInstance(jsonProperty.PropertyType);
+            }
+
+            jsonProperty.ValueProvider.SetValue(target, value);
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryReplace(
+            object target,
+            string segment,
+            IContractResolver
+            contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!jsonProperty.Writable)
+            {
+                errorMessage = Resources.FormatCannotUpdateProperty(segment);
+                return false;
+            }
+
+            if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue))
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                return false;
+            }
+
+            jsonProperty.ValueProvider.SetValue(target, convertedValue);
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryTest(
+            object target,
+            string segment,
+            IContractResolver
+            contractResolver,
+            object value,
+            out string errorMessage)
+        {
+            if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+                return false;
+            }
+
+            if (!jsonProperty.Readable)
+            {
+                errorMessage = Resources.FormatCannotReadProperty(segment);
+                return false;
+            }
+
+            if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue))
+            {
+                errorMessage = Resources.FormatInvalidValueForProperty(value);
+                return false;
+            }
+
+            var currentValue = jsonProperty.ValueProvider.GetValue(target);
+            if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue)))
+            {
+                errorMessage = Resources.FormatValueNotEqualToTestValue(currentValue, value, segment);
+                return false;
+            }
+
+            errorMessage = null;
+            return true;
+        }
+
+        public virtual bool TryTraverse(
+            object target,
+            string segment,
+            IContractResolver contractResolver,
+            out object value,
+            out string errorMessage)
+        {
+            if (target == null)
+            {
+                value = null;
+                errorMessage = null;
+                return false;
+            }
+
+            if (TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty))
+            {
+                value = jsonProperty.ValueProvider.GetValue(target);
+                errorMessage = null;
+                return true;
+            }
+
+            value = null;
+            errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
+            return false;
+        }
+
+        protected virtual bool TryGetJsonProperty(
+            object target,
+            IContractResolver contractResolver,
+            string segment,
+            out JsonProperty jsonProperty)
+        {
+            if (contractResolver.ResolveContract(target.GetType()) is JsonObjectContract jsonObjectContract)
+            {
+                var pocoProperty = jsonObjectContract
+                    .Properties
+                    .FirstOrDefault(p => string.Equals(p.PropertyName, segment, StringComparison.OrdinalIgnoreCase));
+
+                if (pocoProperty != null)
+                {
+                    jsonProperty = pocoProperty;
+                    return true;
+                }
+            }
+
+            jsonProperty = null;
+            return false;
+        }
+
+        protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue)
+        {
+            var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType);
+            if (!conversionResult.CanBeConverted)
+            {
+                convertedValue = null;
+                return false;
+            }
+
+            convertedValue = conversionResult.ConvertedInstance;
+            return true;
+        }
+    }
+}

+ 271 - 0
src/Features/JsonPatch/src/JsonPatchDocument.cs

@@ -0,0 +1,271 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Adapters;
+using Microsoft.AspNetCore.JsonPatch.Converters;
+using Microsoft.AspNetCore.JsonPatch.Exceptions;
+using Microsoft.AspNetCore.JsonPatch.Internal;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    // Implementation details: the purpose of this type of patch document is to allow creation of such
+    // documents for cases where there's no class/DTO to work on. Typical use case: backend not built in
+    // .NET or architecture doesn't contain a shared DTO layer.
+    [JsonConverter(typeof(JsonPatchDocumentConverter))]
+    public class JsonPatchDocument : IJsonPatchDocument
+    {
+        public List<Operation> Operations { get; private set; }
+
+        [JsonIgnore]
+        public IContractResolver ContractResolver { get; set; }
+
+        public JsonPatchDocument()
+        {
+            Operations = new List<Operation>();
+            ContractResolver = new DefaultContractResolver();
+        }
+
+        public JsonPatchDocument(List<Operation> operations, IContractResolver contractResolver)
+        {
+            if (operations == null)
+            {
+                throw new ArgumentNullException(nameof(operations));
+            }
+
+            if (contractResolver == null)
+            {
+                throw new ArgumentNullException(nameof(contractResolver));
+            }
+
+            Operations = operations;
+            ContractResolver = contractResolver;
+        }
+
+        /// <summary>
+        /// Add operation.  Will result in, for example,
+        /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument Add(string path, object value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("add", PathHelpers.ValidateAndNormalizePath(path), null, value));
+            return this;
+        }
+
+        /// <summary>
+        /// Remove value at target location.  Will result in, for example,
+        /// { "op": "remove", "path": "/a/b/c" }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument Remove(string path)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("remove", PathHelpers.ValidateAndNormalizePath(path), null, null));
+            return this;
+        }
+
+        /// <summary>
+        /// Replace value.  Will result in, for example,
+        /// { "op": "replace", "path": "/a/b/c", "value": 42 }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument Replace(string path, object value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("replace", PathHelpers.ValidateAndNormalizePath(path), null, value));
+            return this;
+        }
+
+        /// <summary>
+        /// Test value.  Will result in, for example,
+        /// { "op": "test", "path": "/a/b/c", "value": 42 }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument Test(string path, object value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("test", PathHelpers.ValidateAndNormalizePath(path), null, value));
+            return this;
+        }
+
+        /// <summary>
+        /// Removes value at specified location and add it to the target location.  Will result in, for example:
+        /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
+        /// </summary>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument Move(string from, string path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("move", PathHelpers.ValidateAndNormalizePath(path), PathHelpers.ValidateAndNormalizePath(from)));
+            return this;
+        }
+
+        /// <summary>
+        /// Copy the value at specified location to the target location.  Will result in, for example:
+        /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
+        /// </summary>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument Copy(string from, string path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation("copy", PathHelpers.ValidateAndNormalizePath(path), PathHelpers.ValidateAndNormalizePath(from)));
+            return this;
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        public void ApplyTo(object objectToApplyTo)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory()));
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="logErrorAction">Action to log errors</param>
+        public void ApplyTo(object objectToApplyTo, Action<JsonPatchError> logErrorAction)
+        {
+            ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction);
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="adapter">IObjectAdapter instance to use when applying</param>
+        /// <param name="logErrorAction">Action to log errors</param>
+        public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter, Action<JsonPatchError> logErrorAction)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            foreach (var op in Operations)
+            {
+                try
+                {
+                    op.Apply(objectToApplyTo, adapter);
+                }
+                catch (JsonPatchException jsonPatchException)
+                {
+                    var errorReporter = logErrorAction ?? ErrorReporter.Default;
+                    errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message));
+
+                    // As per JSON Patch spec if an operation results in error, further operations should not be executed.
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="adapter">IObjectAdapter instance to use when applying</param>
+        public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            // apply each operation in order
+            foreach (var op in Operations)
+            {
+                op.Apply(objectToApplyTo, adapter);
+            }
+        }
+
+        IList<Operation> IJsonPatchDocument.GetOperations()
+        {
+            var allOps = new List<Operation>();
+
+            if (Operations != null)
+            {
+                foreach (var op in Operations)
+                {
+                    var untypedOp = new Operation();
+
+                    untypedOp.op = op.op;
+                    untypedOp.value = op.value;
+                    untypedOp.path = op.path;
+                    untypedOp.from = op.from;
+
+                    allOps.Add(untypedOp);
+                }
+            }
+
+            return allOps;
+        }
+    }
+}

+ 884 - 0
src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs

@@ -0,0 +1,884 @@
+// 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.Collections.Generic;
+using System.Globalization;
+using System.Linq.Expressions;
+using Microsoft.AspNetCore.JsonPatch.Adapters;
+using Microsoft.AspNetCore.JsonPatch.Converters;
+using Microsoft.AspNetCore.JsonPatch.Exceptions;
+using Microsoft.AspNetCore.JsonPatch.Internal;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    // Implementation details: the purpose of this type of patch document is to ensure we can do type-checking
+    // when producing a JsonPatchDocument.  However, we cannot send this "typed" over the wire, as that would require
+    // including type data in the JsonPatchDocument serialized as JSON (to allow for correct deserialization) - that's
+    // not according to RFC 6902, and would thus break cross-platform compatibility.
+    [JsonConverter(typeof(TypedJsonPatchDocumentConverter))]
+    public class JsonPatchDocument<TModel> : IJsonPatchDocument where TModel : class
+    {
+        public List<Operation<TModel>> Operations { get; private set; }
+
+        [JsonIgnore]
+        public IContractResolver ContractResolver { get; set; }
+
+        public JsonPatchDocument()
+        {
+            Operations = new List<Operation<TModel>>();
+            ContractResolver = new DefaultContractResolver();
+        }
+
+        // Create from list of operations
+        public JsonPatchDocument(List<Operation<TModel>> operations, IContractResolver contractResolver)
+        {
+            Operations = operations ?? throw new ArgumentNullException(nameof(operations));
+            ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
+        }
+
+        /// <summary>
+        /// Add operation.  Will result in, for example,
+        /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Add<TProp>(Expression<Func<TModel, TProp>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "add",
+                GetPath(path, null),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Add value to list at given position
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <param name="position">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Add<TProp>(
+            Expression<Func<TModel, IList<TProp>>> path,
+            TProp value,
+            int position)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "add",
+                GetPath(path, position.ToString()),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Add value to the end of the list
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Add<TProp>(Expression<Func<TModel, IList<TProp>>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "add",
+                GetPath(path, "-"),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Remove value at target location.  Will result in, for example,
+        /// { "op": "remove", "path": "/a/b/c" }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Remove<TProp>(Expression<Func<TModel, TProp>> path)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>("remove", GetPath(path, null), from: null));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Remove value from list at given position
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="position">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Remove<TProp>(Expression<Func<TModel, IList<TProp>>> path, int position)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "remove",
+                GetPath(path, position.ToString()),
+                from: null));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Remove value from end of list
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Remove<TProp>(Expression<Func<TModel, IList<TProp>>> path)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "remove",
+                GetPath(path, "-"),
+                from: null));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Replace value.  Will result in, for example,
+        /// { "op": "replace", "path": "/a/b/c", "value": 42 }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Replace<TProp>(Expression<Func<TModel, TProp>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "replace",
+                GetPath(path, null),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Replace value in a list at given position
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <param name="position">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Replace<TProp>(Expression<Func<TModel, IList<TProp>>> path,
+            TProp value, int position)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "replace",
+                GetPath(path, position.ToString()),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Replace value at end of a list
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Replace<TProp>(Expression<Func<TModel, IList<TProp>>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "replace",
+                GetPath(path, "-"),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Test value.  Will result in, for example,
+        /// { "op": "test", "path": "/a/b/c", "value": 42 }
+        /// </summary>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Test<TProp>(Expression<Func<TModel, TProp>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "test",
+                GetPath(path, null),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Test value in a list at given position
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <param name="position">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Test<TProp>(Expression<Func<TModel, IList<TProp>>> path,
+            TProp value, int position)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "test",
+                GetPath(path, position.ToString()),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Test value at end of a list
+        /// </summary>
+        /// <typeparam name="TProp">value type</typeparam>
+        /// <param name="path">target location</param>
+        /// <param name="value">value</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Test<TProp>(Expression<Func<TModel, IList<TProp>>> path, TProp value)
+        {
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "test",
+                GetPath(path, "-"),
+                from: null,
+                value: value));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Removes value at specified location and add it to the target location.  Will result in, for example:
+        /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
+        /// </summary>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+            Expression<Func<TModel, TProp>> from,
+            Expression<Func<TModel, TProp>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, null),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Move from a position in a list to a new location
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+            Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+            Expression<Func<TModel, TProp>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, null),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Move from a property to a location in a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <param name="positionTo">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+            Expression<Func<TModel, TProp>> from,
+            Expression<Func<TModel, IList<TProp>>> path,
+            int positionTo)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, positionTo.ToString()),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Move from a position in a list to another location in a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position (source)</param>
+        /// <param name="path">target location</param>
+        /// <param name="positionTo">position (target)</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+            Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+            Expression<Func<TModel, IList<TProp>>> path,
+            int positionTo)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, positionTo.ToString()),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Move from a position in a list to the end of another list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+            Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+            Expression<Func<TModel, IList<TProp>>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, "-"),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Move to the end of a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Move<TProp>(
+           Expression<Func<TModel, TProp>> from,
+           Expression<Func<TModel, IList<TProp>>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "move",
+                GetPath(path, "-"),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy the value at specified location to the target location.  Willr esult in, for example:
+        /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
+        /// </summary>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+           Expression<Func<TModel, TProp>> from,
+           Expression<Func<TModel, TProp>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, null),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy from a position in a list to a new location
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+           Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+           Expression<Func<TModel, TProp>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, null),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy from a property to a location in a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <param name="positionTo">position</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+            Expression<Func<TModel, TProp>> from,
+            Expression<Func<TModel, IList<TProp>>> path,
+            int positionTo)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, positionTo.ToString()),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy from a position in a list to a new location in a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position (source)</param>
+        /// <param name="path">target location</param>
+        /// <param name="positionTo">position (target)</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+            Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+            Expression<Func<TModel, IList<TProp>>> path,
+            int positionTo)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, positionTo.ToString()),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy from a position in a list to the end of another list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="positionFrom">position</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+            Expression<Func<TModel, IList<TProp>>> from,
+            int positionFrom,
+            Expression<Func<TModel, IList<TProp>>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, "-"),
+                GetPath(from, positionFrom.ToString())));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Copy to the end of a list
+        /// </summary>
+        /// <typeparam name="TProp"></typeparam>
+        /// <param name="from">source location</param>
+        /// <param name="path">target location</param>
+        /// <returns></returns>
+        public JsonPatchDocument<TModel> Copy<TProp>(
+            Expression<Func<TModel, TProp>> from,
+            Expression<Func<TModel, IList<TProp>>> path)
+        {
+            if (from == null)
+            {
+                throw new ArgumentNullException(nameof(from));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            Operations.Add(new Operation<TModel>(
+                "copy",
+                GetPath(path, "-"),
+                GetPath(from, null)));
+
+            return this;
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        public void ApplyTo(TModel objectToApplyTo)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory()));
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="logErrorAction">Action to log errors</param>
+        public void ApplyTo(TModel objectToApplyTo, Action<JsonPatchError> logErrorAction)
+        {
+            ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction);
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="adapter">IObjectAdapter instance to use when applying</param>
+        /// <param name="logErrorAction">Action to log errors</param>
+        public void ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter, Action<JsonPatchError> logErrorAction)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            foreach (var op in Operations)
+            {
+                try
+                {
+                    op.Apply(objectToApplyTo, adapter);
+                }
+                catch (JsonPatchException jsonPatchException)
+                {
+                    var errorReporter = logErrorAction ?? ErrorReporter.Default;
+                    errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message));
+
+                    // As per JSON Patch spec if an operation results in error, further operations should not be executed.
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Apply this JsonPatchDocument
+        /// </summary>
+        /// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
+        /// <param name="adapter">IObjectAdapter instance to use when applying</param>
+        public void ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            // apply each operation in order
+            foreach (var op in Operations)
+            {
+                op.Apply(objectToApplyTo, adapter);
+            }
+        }
+
+        IList<Operation> IJsonPatchDocument.GetOperations()
+        {
+            var allOps = new List<Operation>();
+
+            if (Operations != null)
+            {
+                foreach (var op in Operations)
+                {
+                    var untypedOp = new Operation
+                    {
+                        op = op.op,
+                        value = op.value,
+                        path = op.path,
+                        from = op.from
+                    };
+
+                    allOps.Add(untypedOp);
+                }
+            }
+
+            return allOps;
+        }
+
+        // Internal for testing
+        internal string GetPath<TProp>(Expression<Func<TModel, TProp>> expr, string position)
+        {
+            var segments = GetPathSegments(expr.Body);
+            var path = String.Join("/", segments);
+            if (position != null)
+            {
+                path += "/" + position;
+                if (segments.Count == 0)
+                {
+                    return path;
+                }
+            }
+
+            return "/" + path;
+        }
+
+        private List<string> GetPathSegments(Expression expr)
+        {
+            var listOfSegments = new List<string>();
+            switch (expr.NodeType)
+            {
+                case ExpressionType.ArrayIndex:
+                    var binaryExpression = (BinaryExpression)expr;
+                    listOfSegments.AddRange(GetPathSegments(binaryExpression.Left));
+                    listOfSegments.Add(binaryExpression.Right.ToString());
+                    return listOfSegments;
+
+                case ExpressionType.Call:
+                    var methodCallExpression = (MethodCallExpression)expr;
+                    listOfSegments.AddRange(GetPathSegments(methodCallExpression.Object));
+                    listOfSegments.Add(EvaluateExpression(methodCallExpression.Arguments[0]));
+                    return listOfSegments;
+
+                case ExpressionType.Convert:
+                    listOfSegments.AddRange(GetPathSegments(((UnaryExpression)expr).Operand));
+                    return listOfSegments;
+
+                case ExpressionType.MemberAccess:
+                    var memberExpression = expr as MemberExpression;
+                    listOfSegments.AddRange(GetPathSegments(memberExpression.Expression));
+                    // Get property name, respecting JsonProperty attribute
+                    listOfSegments.Add(GetPropertyNameFromMemberExpression(memberExpression));
+                    return listOfSegments;
+
+                case ExpressionType.Parameter:
+                    // Fits "x => x" (the whole document which is "" as JSON pointer)
+                    return listOfSegments;
+
+                default:
+                    throw new InvalidOperationException(Resources.FormatExpressionTypeNotSupported(expr));
+            }
+        }
+
+        private string GetPropertyNameFromMemberExpression(MemberExpression memberExpression)
+        {
+            var jsonObjectContract = ContractResolver.ResolveContract(memberExpression.Expression.Type) as JsonObjectContract;
+            if (jsonObjectContract != null)
+            {
+                return jsonObjectContract.Properties
+                    .First(jsonProperty => jsonProperty.UnderlyingName == memberExpression.Member.Name)
+                    .PropertyName;
+            }
+
+            return null;
+        }
+
+        private static bool ContinueWithSubPath(ExpressionType expressionType)
+        {
+            return (expressionType == ExpressionType.ArrayIndex
+                || expressionType == ExpressionType.Call
+                || expressionType == ExpressionType.Convert
+                || expressionType == ExpressionType.MemberAccess);
+
+        }
+
+        // Evaluates the value of the key or index which may be an int or a string, 
+        // or some other expression type.
+        // The expression is converted to a delegate and the result of executing the delegate is returned as a string.
+        private static string EvaluateExpression(Expression expression)
+        {
+            var converted = Expression.Convert(expression, typeof(object));
+            var fakeParameter = Expression.Parameter(typeof(object), null);
+            var lambda = Expression.Lambda<Func<object, object>>(converted, fakeParameter);
+            var func = lambda.Compile();
+
+            return Convert.ToString(func(null), CultureInfo.InvariantCulture);
+        }
+    }
+}

+ 50 - 0
src/Features/JsonPatch/src/JsonPatchError.cs

@@ -0,0 +1,50 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Operations;
+
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    /// <summary>
+    /// Captures error message and the related entity and the operation that caused it.
+    /// </summary>
+    public class JsonPatchError
+    {
+        /// <summary>
+        /// Initializes a new instance of <see cref="JsonPatchError"/>.
+        /// </summary>
+        /// <param name="affectedObject">The object that is affected by the error.</param>
+        /// <param name="operation">The <see cref="Operation"/> that caused the error.</param>
+        /// <param name="errorMessage">The error message.</param>
+        public JsonPatchError(
+            object affectedObject,
+            Operation operation,
+            string errorMessage)
+        {
+            if (errorMessage == null)
+            {
+                throw new ArgumentNullException(nameof(errorMessage));
+            }
+
+            AffectedObject = affectedObject;
+            Operation = operation;
+            ErrorMessage = errorMessage;
+        }
+
+        /// <summary>
+        /// Gets the object that is affected by the error.
+        /// </summary>
+        public object AffectedObject { get; }
+
+        /// <summary>
+        /// Gets the <see cref="Operation"/> that caused the error.
+        /// </summary>
+        public Operation Operation { get; }
+
+        /// <summary>
+        /// Gets the error message.
+        /// </summary>
+        public string ErrorMessage { get; }
+    }
+}

+ 17 - 0
src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <Description>ASP.NET Core support for JSON PATCH.</Description>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <NoWarn>$(NoWarn);CS1591</NoWarn>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <PackageTags>aspnetcore;json;jsonpatch</PackageTags>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="Newtonsoft.Json" />
+    <Reference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" />
+  </ItemGroup>
+
+</Project>

+ 82 - 0
src/Features/JsonPatch/src/Operations/Operation.cs

@@ -0,0 +1,82 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Adapters;
+using Newtonsoft.Json;
+
+namespace Microsoft.AspNetCore.JsonPatch.Operations
+{
+    public class Operation : OperationBase
+    {
+        [JsonProperty("value")]
+        public object value { get; set; }
+
+        public Operation()
+        {
+
+        }
+
+        public Operation(string op, string path, string from, object value)
+            : base(op, path, from)
+        {
+            this.value = value;
+        }
+
+        public Operation(string op, string path, string from)
+            : base(op, path, from)
+        {
+        }
+
+        public void Apply(object objectToApplyTo, IObjectAdapter adapter)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            switch (OperationType)
+            {
+                case OperationType.Add:
+                    adapter.Add(this, objectToApplyTo);
+                    break;
+                case OperationType.Remove:
+                    adapter.Remove(this, objectToApplyTo);
+                    break;
+                case OperationType.Replace:
+                    adapter.Replace(this, objectToApplyTo);
+                    break;
+                case OperationType.Move:
+                    adapter.Move(this, objectToApplyTo);
+                    break;
+                case OperationType.Copy:
+                    adapter.Copy(this, objectToApplyTo);
+                    break;
+                case OperationType.Test:
+                    if (adapter is IObjectAdapterWithTest adapterWithTest)
+                    {
+                        adapterWithTest.Test(this, objectToApplyTo);
+                        break;
+                    }
+                    else
+                    {
+                        throw new NotSupportedException(Resources.TestOperationNotSupported);
+                    }
+                default:
+                    break;
+            }
+        }
+
+        public bool ShouldSerializevalue()
+        {
+            return (OperationType == OperationType.Add
+                || OperationType == OperationType.Replace
+                || OperationType == OperationType.Test);
+        }
+    }
+}

+ 76 - 0
src/Features/JsonPatch/src/Operations/OperationBase.cs

@@ -0,0 +1,76 @@
+// 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 Newtonsoft.Json;
+
+namespace Microsoft.AspNetCore.JsonPatch.Operations
+{
+    public class OperationBase
+    {
+        private string _op;
+        private OperationType _operationType;
+
+        [JsonIgnore]
+        public OperationType OperationType
+        {
+            get
+            {
+                return _operationType;
+            }
+        }
+
+        [JsonProperty("path")]
+        public string path { get; set; }
+
+        [JsonProperty("op")]
+        public string op
+        {
+            get
+            {
+                return _op;
+            }
+            set
+            {
+                OperationType result;
+                if (!Enum.TryParse(value, ignoreCase: true, result: out result))
+                {
+                    result = OperationType.Invalid;
+                }
+                _operationType = result;
+                _op = value;
+            }
+        }
+
+        [JsonProperty("from")]
+        public string from { get; set; }
+
+        public OperationBase()
+        {
+
+        }
+
+        public OperationBase(string op, string path, string from)
+        {
+            if (op == null)
+            {
+                throw new ArgumentNullException(nameof(op));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            this.op = op;
+            this.path = path;
+            this.from = from;
+        }
+
+        public bool ShouldSerializefrom()
+        {
+            return (OperationType == OperationType.Move
+                || OperationType == OperationType.Copy);
+        }
+    }
+}

+ 94 - 0
src/Features/JsonPatch/src/Operations/OperationOfT.cs

@@ -0,0 +1,94 @@
+// 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 Microsoft.AspNetCore.JsonPatch.Adapters;
+using Microsoft.AspNetCore.JsonPatch.Exceptions;
+
+namespace Microsoft.AspNetCore.JsonPatch.Operations
+{
+    public class Operation<TModel> : Operation where TModel : class
+    {
+        public Operation()
+        {
+
+        }
+
+        public Operation(string op, string path, string from, object value)
+            : base(op, path, from)
+        {
+            if (op == null)
+            {
+                throw new ArgumentNullException(nameof(op));
+            }
+
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+            this.value = value;
+        }
+
+        public Operation(string op, string path, string from)
+            : base(op, path, from)
+        {
+            if (op == null)
+            {
+                throw new ArgumentNullException(nameof(op));
+            }
+            if (path == null)
+            {
+                throw new ArgumentNullException(nameof(path));
+            }
+
+        }
+
+        public void Apply(TModel objectToApplyTo, IObjectAdapter adapter)
+        {
+            if (objectToApplyTo == null)
+            {
+                throw new ArgumentNullException(nameof(objectToApplyTo));
+            }
+
+            if (adapter == null)
+            {
+                throw new ArgumentNullException(nameof(adapter));
+            }
+
+            switch (OperationType)
+            {
+                case OperationType.Add:
+                    adapter.Add(this, objectToApplyTo);
+                    break;
+                case OperationType.Remove:
+                    adapter.Remove(this, objectToApplyTo);
+                    break;
+                case OperationType.Replace:
+                    adapter.Replace(this, objectToApplyTo);
+                    break;
+                case OperationType.Move:
+                    adapter.Move(this, objectToApplyTo);
+                    break;
+                case OperationType.Copy:
+                    adapter.Copy(this, objectToApplyTo);
+                    break;
+                case OperationType.Test:
+                    if (adapter is IObjectAdapterWithTest adapterWithTest)
+                    {
+                        adapterWithTest.Test(this, objectToApplyTo);
+                        break;
+                    }
+                    else
+                    {
+                        throw new JsonPatchException(new JsonPatchError(objectToApplyTo, this, Resources.TestOperationNotSupported));
+                    }
+                case OperationType.Invalid:
+                    throw new JsonPatchException(
+                        Resources.FormatInvalidJsonPatchOperation(op), innerException: null);
+                default:
+                    break;
+            }
+        }
+    }
+}

+ 16 - 0
src/Features/JsonPatch/src/Operations/OperationType.cs

@@ -0,0 +1,16 @@
+// 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.JsonPatch.Operations
+{
+    public enum OperationType
+    {
+        Add,
+        Remove,
+        Replace,
+        Move,
+        Copy,
+        Test,
+        Invalid
+    }
+}

+ 7 - 0
src/Features/JsonPatch/src/Properties/AssemblyInfo.cs

@@ -0,0 +1,7 @@
+
+// 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.JsonPatch.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

+ 338 - 0
src/Features/JsonPatch/src/Properties/Resources.Designer.cs

@@ -0,0 +1,338 @@
+// <auto-generated />
+namespace Microsoft.AspNetCore.JsonPatch
+{
+    using System.Globalization;
+    using System.Reflection;
+    using System.Resources;
+
+    internal static class Resources
+    {
+        private static readonly ResourceManager _resourceManager
+            = new ResourceManager("Microsoft.AspNetCore.JsonPatch.Resources", typeof(Resources).GetTypeInfo().Assembly);
+
+        /// <summary>
+        /// The property at '{0}' could not be copied.
+        /// </summary>
+        internal static string CannotCopyProperty
+        {
+            get => GetString("CannotCopyProperty");
+        }
+
+        /// <summary>
+        /// The property at '{0}' could not be copied.
+        /// </summary>
+        internal static string FormatCannotCopyProperty(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("CannotCopyProperty"), p0);
+
+        /// <summary>
+        /// The type of the property at path '{0}' could not be determined.
+        /// </summary>
+        internal static string CannotDeterminePropertyType
+        {
+            get => GetString("CannotDeterminePropertyType");
+        }
+
+        /// <summary>
+        /// The type of the property at path '{0}' could not be determined.
+        /// </summary>
+        internal static string FormatCannotDeterminePropertyType(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("CannotDeterminePropertyType"), p0);
+
+        /// <summary>
+        /// The '{0}' operation at path '{1}' could not be performed.
+        /// </summary>
+        internal static string CannotPerformOperation
+        {
+            get => GetString("CannotPerformOperation");
+        }
+
+        /// <summary>
+        /// The '{0}' operation at path '{1}' could not be performed.
+        /// </summary>
+        internal static string FormatCannotPerformOperation(object p0, object p1)
+            => string.Format(CultureInfo.CurrentCulture, GetString("CannotPerformOperation"), p0, p1);
+
+        /// <summary>
+        /// The property at '{0}' could not be read.
+        /// </summary>
+        internal static string CannotReadProperty
+        {
+            get => GetString("CannotReadProperty");
+        }
+
+        /// <summary>
+        /// The property at '{0}' could not be read.
+        /// </summary>
+        internal static string FormatCannotReadProperty(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("CannotReadProperty"), p0);
+
+        /// <summary>
+        /// The property at path '{0}' could not be updated.
+        /// </summary>
+        internal static string CannotUpdateProperty
+        {
+            get => GetString("CannotUpdateProperty");
+        }
+
+        /// <summary>
+        /// The property at path '{0}' could not be updated.
+        /// </summary>
+        internal static string FormatCannotUpdateProperty(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("CannotUpdateProperty"), p0);
+
+        /// <summary>
+        /// The expression '{0}' is not supported. Supported expressions include member access and indexer expressions.
+        /// </summary>
+        internal static string ExpressionTypeNotSupported
+        {
+            get => GetString("ExpressionTypeNotSupported");
+        }
+
+        /// <summary>
+        /// The expression '{0}' is not supported. Supported expressions include member access and indexer expressions.
+        /// </summary>
+        internal static string FormatExpressionTypeNotSupported(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("ExpressionTypeNotSupported"), p0);
+
+        /// <summary>
+        /// The index value provided by path segment '{0}' is out of bounds of the array size.
+        /// </summary>
+        internal static string IndexOutOfBounds
+        {
+            get => GetString("IndexOutOfBounds");
+        }
+
+        /// <summary>
+        /// The index value provided by path segment '{0}' is out of bounds of the array size.
+        /// </summary>
+        internal static string FormatIndexOutOfBounds(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("IndexOutOfBounds"), p0);
+
+        /// <summary>
+        /// The path segment '{0}' is invalid for an array index.
+        /// </summary>
+        internal static string InvalidIndexValue
+        {
+            get => GetString("InvalidIndexValue");
+        }
+
+        /// <summary>
+        /// The path segment '{0}' is invalid for an array index.
+        /// </summary>
+        internal static string FormatInvalidIndexValue(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("InvalidIndexValue"), p0);
+
+        /// <summary>
+        /// The JSON patch document was malformed and could not be parsed.
+        /// </summary>
+        internal static string InvalidJsonPatchDocument
+        {
+            get => GetString("InvalidJsonPatchDocument");
+        }
+
+        /// <summary>
+        /// The JSON patch document was malformed and could not be parsed.
+        /// </summary>
+        internal static string FormatInvalidJsonPatchDocument()
+            => GetString("InvalidJsonPatchDocument");
+
+        /// <summary>
+        /// Invalid JsonPatch operation '{0}'.
+        /// </summary>
+        internal static string InvalidJsonPatchOperation
+        {
+            get => GetString("InvalidJsonPatchOperation");
+        }
+
+        /// <summary>
+        /// Invalid JsonPatch operation '{0}'.
+        /// </summary>
+        internal static string FormatInvalidJsonPatchOperation(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("InvalidJsonPatchOperation"), p0);
+
+        /// <summary>
+        /// The provided path segment '{0}' cannot be converted to the target type.
+        /// </summary>
+        internal static string InvalidPathSegment
+        {
+            get => GetString("InvalidPathSegment");
+        }
+
+        /// <summary>
+        /// The provided path segment '{0}' cannot be converted to the target type.
+        /// </summary>
+        internal static string FormatInvalidPathSegment(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("InvalidPathSegment"), p0);
+
+        /// <summary>
+        /// The provided string '{0}' is an invalid path.
+        /// </summary>
+        internal static string InvalidValueForPath
+        {
+            get => GetString("InvalidValueForPath");
+        }
+
+        /// <summary>
+        /// The provided string '{0}' is an invalid path.
+        /// </summary>
+        internal static string FormatInvalidValueForPath(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("InvalidValueForPath"), p0);
+
+        /// <summary>
+        /// The value '{0}' is invalid for target location.
+        /// </summary>
+        internal static string InvalidValueForProperty
+        {
+            get => GetString("InvalidValueForProperty");
+        }
+
+        /// <summary>
+        /// The value '{0}' is invalid for target location.
+        /// </summary>
+        internal static string FormatInvalidValueForProperty(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("InvalidValueForProperty"), p0);
+
+        /// <summary>
+        /// '{0}' must be of type '{1}'.
+        /// </summary>
+        internal static string ParameterMustMatchType
+        {
+            get => GetString("ParameterMustMatchType");
+        }
+
+        /// <summary>
+        /// '{0}' must be of type '{1}'.
+        /// </summary>
+        internal static string FormatParameterMustMatchType(object p0, object p1)
+            => string.Format(CultureInfo.CurrentCulture, GetString("ParameterMustMatchType"), p0, p1);
+
+        /// <summary>
+        /// The type '{0}' which is an array is not supported for json patch operations as it has a fixed size.
+        /// </summary>
+        internal static string PatchNotSupportedForArrays
+        {
+            get => GetString("PatchNotSupportedForArrays");
+        }
+
+        /// <summary>
+        /// The type '{0}' which is an array is not supported for json patch operations as it has a fixed size.
+        /// </summary>
+        internal static string FormatPatchNotSupportedForArrays(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("PatchNotSupportedForArrays"), p0);
+
+        /// <summary>
+        /// The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported.
+        /// </summary>
+        internal static string PatchNotSupportedForNonGenericLists
+        {
+            get => GetString("PatchNotSupportedForNonGenericLists");
+        }
+
+        /// <summary>
+        /// The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported.
+        /// </summary>
+        internal static string FormatPatchNotSupportedForNonGenericLists(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("PatchNotSupportedForNonGenericLists"), p0);
+
+        /// <summary>
+        /// The target location specified by path segment '{0}' was not found.
+        /// </summary>
+        internal static string TargetLocationAtPathSegmentNotFound
+        {
+            get => GetString("TargetLocationAtPathSegmentNotFound");
+        }
+
+        /// <summary>
+        /// The target location specified by path segment '{0}' was not found.
+        /// </summary>
+        internal static string FormatTargetLocationAtPathSegmentNotFound(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("TargetLocationAtPathSegmentNotFound"), p0);
+
+        /// <summary>
+        /// For operation '{0}', the target location specified by path '{1}' was not found.
+        /// </summary>
+        internal static string TargetLocationNotFound
+        {
+            get => GetString("TargetLocationNotFound");
+        }
+
+        /// <summary>
+        /// For operation '{0}', the target location specified by path '{1}' was not found.
+        /// </summary>
+        internal static string FormatTargetLocationNotFound(object p0, object p1)
+            => string.Format(CultureInfo.CurrentCulture, GetString("TargetLocationNotFound"), p0, p1);
+
+        /// <summary>
+        /// The test operation is not supported.
+        /// </summary>
+        internal static string TestOperationNotSupported
+        {
+            get => GetString("TestOperationNotSupported");
+        }
+
+        /// <summary>
+        /// The test operation is not supported.
+        /// </summary>
+        internal static string FormatTestOperationNotSupported()
+            => GetString("TestOperationNotSupported");
+
+        /// <summary>
+        /// The current value '{0}' at position '{2}' is not equal to the test value '{1}'.
+        /// </summary>
+        internal static string ValueAtListPositionNotEqualToTestValue
+        {
+            get => GetString("ValueAtListPositionNotEqualToTestValue");
+        }
+
+        /// <summary>
+        /// The current value '{0}' at position '{2}' is not equal to the test value '{1}'.
+        /// </summary>
+        internal static string FormatValueAtListPositionNotEqualToTestValue(object p0, object p1, object p2)
+            => string.Format(CultureInfo.CurrentCulture, GetString("ValueAtListPositionNotEqualToTestValue"), p0, p1, p2);
+
+        /// <summary>
+        /// The value at '{0}' cannot be null or empty to perform the test operation.
+        /// </summary>
+        internal static string ValueForTargetSegmentCannotBeNullOrEmpty
+        {
+            get => GetString("ValueForTargetSegmentCannotBeNullOrEmpty");
+        }
+
+        /// <summary>
+        /// The value at '{0}' cannot be null or empty to perform the test operation.
+        /// </summary>
+        internal static string FormatValueForTargetSegmentCannotBeNullOrEmpty(object p0)
+            => string.Format(CultureInfo.CurrentCulture, GetString("ValueForTargetSegmentCannotBeNullOrEmpty"), p0);
+
+        /// <summary>
+        /// The current value '{0}' at path '{2}' is not equal to the test value '{1}'.
+        /// </summary>
+        internal static string ValueNotEqualToTestValue
+        {
+            get => GetString("ValueNotEqualToTestValue");
+        }
+
+        /// <summary>
+        /// The current value '{0}' at path '{2}' is not equal to the test value '{1}'.
+        /// </summary>
+        internal static string FormatValueNotEqualToTestValue(object p0, object p1, object p2)
+            => string.Format(CultureInfo.CurrentCulture, GetString("ValueNotEqualToTestValue"), p0, p1, p2);
+
+        private static string GetString(string name, params string[] formatterNames)
+        {
+            var value = _resourceManager.GetString(name);
+
+            System.Diagnostics.Debug.Assert(value != null);
+
+            if (formatterNames != null)
+            {
+                for (var i = 0; i < formatterNames.Length; i++)
+                {
+                    value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+                }
+            }
+
+            return value;
+        }
+    }
+}

+ 186 - 0
src/Features/JsonPatch/src/Resources.resx

@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="CannotCopyProperty" xml:space="preserve">
+    <value>The property at '{0}' could not be copied.</value>
+  </data>
+  <data name="CannotDeterminePropertyType" xml:space="preserve">
+    <value>The type of the property at path '{0}' could not be determined.</value>
+  </data>
+  <data name="CannotPerformOperation" xml:space="preserve">
+    <value>The '{0}' operation at path '{1}' could not be performed.</value>
+  </data>
+  <data name="CannotReadProperty" xml:space="preserve">
+    <value>The property at '{0}' could not be read.</value>
+  </data>
+  <data name="CannotUpdateProperty" xml:space="preserve">
+    <value>The property at path '{0}' could not be updated.</value>
+  </data>
+  <data name="ExpressionTypeNotSupported" xml:space="preserve">
+    <value>The expression '{0}' is not supported. Supported expressions include member access and indexer expressions.</value>
+  </data>
+  <data name="IndexOutOfBounds" xml:space="preserve">
+    <value>The index value provided by path segment '{0}' is out of bounds of the array size.</value>
+  </data>
+  <data name="InvalidIndexValue" xml:space="preserve">
+    <value>The path segment '{0}' is invalid for an array index.</value>
+  </data>
+  <data name="InvalidJsonPatchDocument" xml:space="preserve">
+    <value>The JSON patch document was malformed and could not be parsed.</value>
+  </data>
+  <data name="InvalidJsonPatchOperation" xml:space="preserve">
+    <value>Invalid JsonPatch operation '{0}'.</value>
+  </data>
+  <data name="InvalidPathSegment" xml:space="preserve">
+    <value>The provided path segment '{0}' cannot be converted to the target type.</value>
+  </data>
+  <data name="InvalidValueForPath" xml:space="preserve">
+    <value>The provided string '{0}' is an invalid path.</value>
+  </data>
+  <data name="InvalidValueForProperty" xml:space="preserve">
+    <value>The value '{0}' is invalid for target location.</value>
+  </data>
+  <data name="ParameterMustMatchType" xml:space="preserve">
+    <value>'{0}' must be of type '{1}'.</value>
+  </data>
+  <data name="PatchNotSupportedForArrays" xml:space="preserve">
+    <value>The type '{0}' which is an array is not supported for json patch operations as it has a fixed size.</value>
+  </data>
+  <data name="PatchNotSupportedForNonGenericLists" xml:space="preserve">
+    <value>The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported.</value>
+  </data>
+  <data name="TargetLocationAtPathSegmentNotFound" xml:space="preserve">
+    <value>The target location specified by path segment '{0}' was not found.</value>
+  </data>
+  <data name="TargetLocationNotFound" xml:space="preserve">
+    <value>For operation '{0}', the target location specified by path '{1}' was not found.</value>
+  </data>
+  <data name="TestOperationNotSupported" xml:space="preserve">
+    <value>The test operation is not supported.</value>
+  </data>
+  <data name="ValueAtListPositionNotEqualToTestValue" xml:space="preserve">
+    <value>The current value '{0}' at position '{2}' is not equal to the test value '{1}'.</value>
+  </data>
+  <data name="ValueForTargetSegmentCannotBeNullOrEmpty" xml:space="preserve">
+    <value>The value at '{0}' cannot be null or empty to perform the test operation.</value>
+  </data>
+  <data name="ValueNotEqualToTestValue" xml:space="preserve">
+    <value>The current value '{0}' at path '{2}' is not equal to the test value '{1}'.</value>
+  </data>
+</root>

+ 1985 - 0
src/Features/JsonPatch/src/baseline.netcore.json

@@ -0,0 +1,1985 @@
+{
+  "AssemblyIdentity": "Microsoft.AspNetCore.JsonPatch, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+  "Types": [
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchProperty",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_Property",
+          "Parameters": [],
+          "ReturnType": "Newtonsoft.Json.Serialization.JsonProperty",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_Property",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "Newtonsoft.Json.Serialization.JsonProperty"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_Parent",
+          "Parameters": [],
+          "ReturnType": "System.Object",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_Parent",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "property",
+              "Type": "Newtonsoft.Json.Serialization.JsonProperty"
+            },
+            {
+              "Name": "parent",
+              "Type": "System.Object"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument",
+      "Visibility": "Public",
+      "Kind": "Interface",
+      "Abstract": true,
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_ContractResolver",
+          "Parameters": [],
+          "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_ContractResolver",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "GetOperations",
+          "Parameters": [],
+          "ReturnType": "System.Collections.Generic.IList<Microsoft.AspNetCore.JsonPatch.Operations.Operation>",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [
+        "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument"
+      ],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_Operations",
+          "Parameters": [],
+          "ReturnType": "System.Collections.Generic.List<Microsoft.AspNetCore.JsonPatch.Operations.Operation>",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_ContractResolver",
+          "Parameters": [],
+          "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_ContractResolver",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Add",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Test",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "logErrorAction",
+              "Type": "System.Action<Microsoft.AspNetCore.JsonPatch.JsonPatchError>"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "adapter",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "operations",
+              "Type": "System.Collections.Generic.List<Microsoft.AspNetCore.JsonPatch.Operations.Operation>"
+            },
+            {
+              "Name": "contractResolver",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [
+        "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument"
+      ],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_Operations",
+          "Parameters": [],
+          "ReturnType": "System.Collections.Generic.List<Microsoft.AspNetCore.JsonPatch.Operations.Operation<T0>>",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_ContractResolver",
+          "Parameters": [],
+          "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_ContractResolver",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Add<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Add<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            },
+            {
+              "Name": "position",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Add<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "position",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            },
+            {
+              "Name": "position",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Test<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Test<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            },
+            {
+              "Name": "position",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Test<T0>",
+          "Parameters": [
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "value",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionTo",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionTo",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionTo",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionTo",
+              "Type": "System.Int32"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            },
+            {
+              "Name": "positionFrom",
+              "Type": "System.Int32"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy<T0>",
+          "Parameters": [
+            {
+              "Name": "from",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, T0>>"
+            },
+            {
+              "Name": "path",
+              "Type": "System.Linq.Expressions.Expression<System.Func<T0, System.Collections.Generic.IList<T0>>>"
+            }
+          ],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument<T0>",
+          "Visibility": "Public",
+          "GenericParameter": [
+            {
+              "ParameterName": "TProp",
+              "ParameterPosition": 0,
+              "BaseTypeOrInterfaces": []
+            }
+          ]
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "T0"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "T0"
+            },
+            {
+              "Name": "logErrorAction",
+              "Type": "System.Action<Microsoft.AspNetCore.JsonPatch.JsonPatchError>"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ApplyTo",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "T0"
+            },
+            {
+              "Name": "adapter",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "operations",
+              "Type": "System.Collections.Generic.List<Microsoft.AspNetCore.JsonPatch.Operations.Operation<T0>>"
+            },
+            {
+              "Name": "contractResolver",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": [
+        {
+          "ParameterName": "TModel",
+          "ParameterPosition": 0,
+          "Class": true,
+          "BaseTypeOrInterfaces": []
+        }
+      ]
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchError",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_AffectedObject",
+          "Parameters": [],
+          "ReturnType": "System.Object",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_Operation",
+          "Parameters": [],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_ErrorMessage",
+          "Parameters": [],
+          "ReturnType": "System.String",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "affectedObject",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "errorMessage",
+              "Type": "System.String"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Operations.Operation",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "BaseType": "Microsoft.AspNetCore.JsonPatch.Operations.OperationBase",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_value",
+          "Parameters": [],
+          "ReturnType": "System.Object",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_value",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Apply",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "adapter",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ShouldSerializevalue",
+          "Parameters": [],
+          "ReturnType": "System.Boolean",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "op",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "from",
+              "Type": "System.String"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "op",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "from",
+              "Type": "System.String"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Operations.OperationBase",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_OperationType",
+          "Parameters": [],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.OperationType",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_path",
+          "Parameters": [],
+          "ReturnType": "System.String",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_path",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_op",
+          "Parameters": [],
+          "ReturnType": "System.String",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_op",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_from",
+          "Parameters": [],
+          "ReturnType": "System.String",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "set_from",
+          "Parameters": [
+            {
+              "Name": "value",
+              "Type": "System.String"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ShouldSerializefrom",
+          "Parameters": [],
+          "ReturnType": "System.Boolean",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "op",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "from",
+              "Type": "System.String"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Operations.Operation<T0>",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "BaseType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "Apply",
+          "Parameters": [
+            {
+              "Name": "objectToApplyTo",
+              "Type": "T0"
+            },
+            {
+              "Name": "adapter",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "op",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "from",
+              "Type": "System.String"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "op",
+              "Type": "System.String"
+            },
+            {
+              "Name": "path",
+              "Type": "System.String"
+            },
+            {
+              "Name": "from",
+              "Type": "System.String"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": [
+        {
+          "ParameterName": "TModel",
+          "ParameterPosition": 0,
+          "Class": true,
+          "BaseTypeOrInterfaces": []
+        }
+      ]
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Operations.OperationType",
+      "Visibility": "Public",
+      "Kind": "Enumeration",
+      "Sealed": true,
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Field",
+          "Name": "Add",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "0"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Remove",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "1"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Replace",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "2"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Move",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "3"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Copy",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "4"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Test",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "5"
+        },
+        {
+          "Kind": "Field",
+          "Name": "Invalid",
+          "Parameters": [],
+          "GenericParameter": [],
+          "Literal": "6"
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Helpers.GetValueResult",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_PropertyValue",
+          "Parameters": [],
+          "ReturnType": "System.Object",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_HasError",
+          "Parameters": [],
+          "ReturnType": "System.Boolean",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "propertyValue",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "hasError",
+              "Type": "System.Boolean"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "BaseType": "System.Exception",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "get_FailedOperation",
+          "Parameters": [],
+          "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_AffectedObject",
+          "Parameters": [],
+          "ReturnType": "System.Object",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "jsonPatchError",
+              "Type": "Microsoft.AspNetCore.JsonPatch.JsonPatchError"
+            },
+            {
+              "Name": "innerException",
+              "Type": "System.Exception"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "jsonPatchError",
+              "Type": "Microsoft.AspNetCore.JsonPatch.JsonPatchError"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "message",
+              "Type": "System.String"
+            },
+            {
+              "Name": "innerException",
+              "Type": "System.Exception"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Converters.JsonPatchDocumentConverter",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "BaseType": "Newtonsoft.Json.JsonConverter",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "CanConvert",
+          "Parameters": [
+            {
+              "Name": "objectType",
+              "Type": "System.Type"
+            }
+          ],
+          "ReturnType": "System.Boolean",
+          "Virtual": true,
+          "Override": true,
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "ReadJson",
+          "Parameters": [
+            {
+              "Name": "reader",
+              "Type": "Newtonsoft.Json.JsonReader"
+            },
+            {
+              "Name": "objectType",
+              "Type": "System.Type"
+            },
+            {
+              "Name": "existingValue",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "serializer",
+              "Type": "Newtonsoft.Json.JsonSerializer"
+            }
+          ],
+          "ReturnType": "System.Object",
+          "Virtual": true,
+          "Override": true,
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "WriteJson",
+          "Parameters": [
+            {
+              "Name": "writer",
+              "Type": "Newtonsoft.Json.JsonWriter"
+            },
+            {
+              "Name": "value",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "serializer",
+              "Type": "Newtonsoft.Json.JsonSerializer"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Virtual": true,
+          "Override": true,
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Converters.TypedJsonPatchDocumentConverter",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "BaseType": "Microsoft.AspNetCore.JsonPatch.Converters.JsonPatchDocumentConverter",
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "ReadJson",
+          "Parameters": [
+            {
+              "Name": "reader",
+              "Type": "Newtonsoft.Json.JsonReader"
+            },
+            {
+              "Name": "objectType",
+              "Type": "System.Type"
+            },
+            {
+              "Name": "existingValue",
+              "Type": "System.Object"
+            },
+            {
+              "Name": "serializer",
+              "Type": "Newtonsoft.Json.JsonSerializer"
+            }
+          ],
+          "ReturnType": "System.Object",
+          "Virtual": true,
+          "Override": true,
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+      "Visibility": "Public",
+      "Kind": "Interface",
+      "Abstract": true,
+      "ImplementedInterfaces": [],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "Add",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest",
+      "Visibility": "Public",
+      "Kind": "Interface",
+      "Abstract": true,
+      "ImplementedInterfaces": [
+        "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter"
+      ],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "Test",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    },
+    {
+      "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter",
+      "Visibility": "Public",
+      "Kind": "Class",
+      "ImplementedInterfaces": [
+        "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest"
+      ],
+      "Members": [
+        {
+          "Kind": "Method",
+          "Name": "Add",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Copy",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Move",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Remove",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Replace",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_ContractResolver",
+          "Parameters": [],
+          "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "get_LogErrorAction",
+          "Parameters": [],
+          "ReturnType": "System.Action<Microsoft.AspNetCore.JsonPatch.JsonPatchError>",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Method",
+          "Name": "Test",
+          "Parameters": [
+            {
+              "Name": "operation",
+              "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation"
+            },
+            {
+              "Name": "objectToApplyTo",
+              "Type": "System.Object"
+            }
+          ],
+          "ReturnType": "System.Void",
+          "Sealed": true,
+          "Virtual": true,
+          "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest",
+          "Visibility": "Public",
+          "GenericParameter": []
+        },
+        {
+          "Kind": "Constructor",
+          "Name": ".ctor",
+          "Parameters": [
+            {
+              "Name": "contractResolver",
+              "Type": "Newtonsoft.Json.Serialization.IContractResolver"
+            },
+            {
+              "Name": "logErrorAction",
+              "Type": "System.Action<Microsoft.AspNetCore.JsonPatch.JsonPatchError>"
+            }
+          ],
+          "Visibility": "Public",
+          "GenericParameter": []
+        }
+      ],
+      "GenericParameters": []
+    }
+  ]
+}

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