diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index db2f14248f68330f38618bf7d502699c1d40c6cf..a2d59b873da0dedb407775967c567da88a5cf8cd 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -57,235 +57,249 @@
       <Uri>https://github.com/dotnet/efcore</Uri>
       <Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
+    <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.0" Pinned="true">
+      <Uri>https://github.com/aspnet/Extensions</Uri>
+      <Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
+    </Dependency>
+    <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.0" Pinned="true">
+      <Uri>https://github.com/aspnet/Extensions</Uri>
+      <Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
+    </Dependency>
+    <Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.0" Pinned="true">
+      <Uri>https://github.com/aspnet/Extensions</Uri>
+      <Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" Pinned="true">
+      <Uri>https://github.com/aspnet/Extensions</Uri>
+      <Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
+    </Dependency>
+    <Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Http" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
-    </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Options" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Options" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.JSInterop" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.JSInterop" Version="3.1.2"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
-    <Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
@@ -379,7 +393,7 @@
     </Dependency>
     <Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging">
       <Uri>https://github.com/dotnet/core-setup</Uri>
-      <Sha>916b5cba268e1e1e803243004f4276cf40b2dda8</Sha>
+      <Sha>65f04fb6db7a5e198d05dbebd5c4ad21eb018f89</Sha>
     </Dependency>
     <!--
          Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
@@ -413,7 +427,7 @@
       <Uri>https://github.com/dotnet/corefx</Uri>
       <Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha>
     </Dependency>
-    <Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
@@ -429,7 +443,7 @@
       <Uri>https://github.com/dotnet/arcade</Uri>
       <Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
+    <Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6"  Pinned="true">
       <Uri>https://github.com/dotnet/extensions</Uri>
       <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
     </Dependency>
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs
index cbe0fe363a67ce3870dd42c6ba0fc3fb9968b164..394ccbe7187197361c4ee680d1cdd87637ba6b03 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs
@@ -3,7 +3,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Net;
@@ -12,7 +11,9 @@ using System.Runtime.InteropServices;
 using System.Text.Json;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
-using WsProxy;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using WebAssembly.Net.Debugging;
 
 namespace Microsoft.AspNetCore.Builder
 {
@@ -50,7 +51,8 @@ namespace Microsoft.AspNetCore.Builder
 
                 if (requestPath.Equals("/_framework/debug/ws-proxy", StringComparison.OrdinalIgnoreCase))
                 {
-                    return DebugWebSocketProxyRequest(context);
+                    var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
+                    return DebugWebSocketProxyRequest(loggerFactory, context);
                 }
 
                 if (requestPath.Equals("/_framework/debug", StringComparison.OrdinalIgnoreCase))
@@ -111,7 +113,8 @@ namespace Microsoft.AspNetCore.Builder
                         var proxiedTabInfos = availableTabs.Select(tab =>
                         {
                             var underlyingV8Endpoint = tab.WebSocketDebuggerUrl;
-                            var proxiedV8Endpoint = $"ws://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
+                            var proxiedScheme = request.IsHttps ? "wss" : "ws";
+                            var proxiedV8Endpoint = $"{proxiedScheme}://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
                             return new
                             {
                                 description = "",
@@ -129,10 +132,42 @@ namespace Microsoft.AspNetCore.Builder
                     }
                     else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
                     {
-                        var browserVersionJson = await GetBrowserVersionInfoAsync();
+                        // VS Code's "js-debug" nightly extension, when configured to use the "pwa-chrome"
+                        // debug type, uses the /json/version endpoint to find the websocket endpoint for
+                        // debugging the browser that listens on a user-specified port.
+                        //
+                        // To make this flow work with the Mono debug proxy, we pass the request through
+                        // to the underlying browser (to get its actual version info) but then overwrite
+                        // the "webSocketDebuggerUrl" with the URL to the proxy.
+                        //
+                        // This whole connection flow isn't very good because it doesn't have any way
+                        // to specify the debug port for the underlying browser. So, we end up assuming
+                        // the default port 9222 in all cases. This is good enough for a manual "attach"
+                        // but isn't good enough if the IDE is responsible for launching the browser,
+                        // as it will be on a random port. So,
+                        //
+                        //  - VS isn't going to use this. Instead it will use a configured "debugEndpoint"
+                        //    property from which it can construct the proxy URL directly (including adding
+                        //    a "browser" querystring value to specify the underlying endpoint), bypassing
+                        //    /json/version altogether
+                        //  - We will need to update the VS Code debug adapter to make it do the same as VS
+                        //    if there is a "debugEndpoint" property configured
+                        //
+                        // Once both VS and VS Code support the "debugEndpoint" flow, we should be able to
+                        // remove this /json/version code altogether. We should check that in-browser
+                        // debugging still works at that point.
+
+                        var browserVersionJsonStream = await GetBrowserVersionInfoAsync();
+                        var browserVersion = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(browserVersionJsonStream);
+
+                        if (browserVersion.TryGetValue("webSocketDebuggerUrl", out var browserEndpoint))
+                        {
+                            var proxyEndpoint = GetProxyEndpoint(request, ((JsonElement)browserEndpoint).GetString());
+                            browserVersion["webSocketDebuggerUrl"] = proxyEndpoint;
+                        }
 
                         context.Response.ContentType = "application/json";
-                        await context.Response.WriteAsync(browserVersionJson);
+                        await JsonSerializer.SerializeAsync(context.Response.Body, browserVersion);
                     }
                 }
                 else
@@ -142,7 +177,7 @@ namespace Microsoft.AspNetCore.Builder
             });
         }
 
-        private static async Task DebugWebSocketProxyRequest(HttpContext context)
+        private static async Task DebugWebSocketProxyRequest(ILoggerFactory loggerFactory, HttpContext context)
         {
             if (!context.WebSockets.IsWebSocketRequest)
             {
@@ -152,7 +187,7 @@ namespace Microsoft.AspNetCore.Builder
 
             var browserUri = new Uri(context.Request.Query["browser"]);
             var ideSocket = await context.WebSockets.AcceptWebSocketAsync();
-            await new MonoProxy().Run(browserUri, ideSocket);
+            await new MonoProxy(loggerFactory).Run(browserUri, ideSocket);
         }
 
         private static async Task DebugHome(HttpContext context)
@@ -235,13 +270,30 @@ namespace Microsoft.AspNetCore.Builder
             // page and redirect there
             var tabToDebug = matchingTabs.Single();
             var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
-            var proxyEndpoint = $"{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
+            var proxyEndpoint = GetProxyEndpoint(request, underlyingV8Endpoint);
             var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
-            var wsParamName = request.IsHttps ? "wss" : "ws";
-            var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}";
+            var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{proxyEndpoint.Scheme}={proxyEndpoint.Authority}{proxyEndpoint.PathAndQuery}";
             context.Response.Redirect(devToolsUrlWithProxy);
         }
 
+        private static Uri GetProxyEndpoint(HttpRequest incomingRequest, string browserEndpoint)
+        {
+            var builder = new UriBuilder(
+                schemeName: incomingRequest.IsHttps ? "wss" : "ws",
+                hostName: incomingRequest.Host.Host)
+            {
+                Path = $"{incomingRequest.PathBase}/_framework/debug/ws-proxy",
+                Query = $"browser={WebUtility.UrlEncode(browserEndpoint)}"
+            };
+
+            if (incomingRequest.Host.Port.HasValue)
+            {
+                builder.Port = incomingRequest.Host.Port.Value;
+            }
+
+            return builder.Uri;
+        }
+
         private static string GetLaunchChromeInstructions(string appRootUrl)
         {
             var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
@@ -289,11 +341,12 @@ namespace Microsoft.AspNetCore.Builder
             }
         }
 
-        private static async Task<string> GetBrowserVersionInfoAsync()
+        private static async Task<Stream> GetBrowserVersionInfoAsync()
         {
             using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
             var debuggerHost = GetDebuggerHost();
-            return await httpClient.GetStringAsync($"{debuggerHost}/json/version");
+            var response = await httpClient.GetAsync($"{debuggerHost}/json/version");
+            return await response.Content.ReadAsStreamAsync();
         }
 
         private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
index e1e9b7392ba854634cce785eb6fe9e7329a5af04..5c46bc7324a37effff160f2910c1074ffa5a6ab0 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
@@ -9,8 +9,10 @@ using System.Net.Http;
 using Mono.Cecil.Pdb;
 using Newtonsoft.Json;
 using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Threading;
 
-namespace WsProxy {
+namespace WebAssembly.Net.Debugging {
 	internal class BreakPointRequest {
 		public string Assembly { get; private set; }
 		public string File { get; private set; }
@@ -23,13 +25,15 @@ namespace WsProxy {
 
 		public static BreakPointRequest Parse (JObject args, DebugStore store)
 		{
-			if (args == null)
+			// Events can potentially come out of order, so DebugStore may not be initialized
+			// The BP being set in these cases are JS ones, which we can safely ignore
+			if (args == null || store == null)
 				return null;
 
 			var url = args? ["url"]?.Value<string> ();
 			if (url == null) {
 				var urlRegex = args?["urlRegex"].Value<string>();
-				var sourceFile = store.GetFileByUrlRegex (urlRegex);
+				var sourceFile = store?.GetFileByUrlRegex (urlRegex);
 
 				url = sourceFile?.DotNetUrl;
 			}
@@ -72,7 +76,6 @@ namespace WsProxy {
 		}
 	}
 
-
 	internal class VarInfo {
 		public VarInfo (VariableDebugInformation v)
 		{
@@ -85,10 +88,10 @@ namespace WsProxy {
 			this.Name = p.Name;
 			this.Index = (p.Index + 1) * -1;
 		}
+
 		public string Name { get; private set; }
 		public int Index { get; private set; }
 
-
 		public override string ToString ()
 		{
 			return $"(var-info [{Index}] '{Name}')";
@@ -97,18 +100,14 @@ namespace WsProxy {
 
 
 	internal class CliLocation {
-
-		private MethodInfo method;
-		private int offset;
-
 		public CliLocation (MethodInfo method, int offset)
 		{
-			this.method = method;
-			this.offset = offset;
+			Method = method;
+			Offset = offset;
 		}
 
-		public MethodInfo Method { get => method; }
-		public int Offset { get => offset; }
+		public MethodInfo Method { get; private set; }
+		public int Offset { get; private set; }
 	}
 
 
@@ -282,7 +281,6 @@ namespace WsProxy {
 				.Where (v => !v.IsDebuggerHidden)
 				.Select (v => new VarInfo (v)));
 
-
 			return res.ToArray ();
 		}
 	}
@@ -294,20 +292,21 @@ namespace WsProxy {
 		Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
 		Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
 		readonly List<SourceFile> sources = new List<SourceFile>();
+		internal string Url { get; private set; }
 
-		public AssemblyInfo (byte[] assembly, byte[] pdb)
+		public AssemblyInfo (string url, byte[] assembly, byte[] pdb)
 		{
 			lock (typeof (AssemblyInfo)) {
 				this.id = ++next_id;
 			}
 
 			try {
+				Url = url;
 				ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
-				if (pdb != null) {
-					rp.ReadSymbols = true;
-					rp.SymbolReaderProvider = new PortablePdbReaderProvider ();
+				rp.ReadSymbols = true;
+				rp.SymbolReaderProvider = new PdbReaderProvider ();
+				if (pdb != null)
 					rp.SymbolStream = new MemoryStream (pdb);
-				}
 
 				rp.ReadingMode = ReadingMode.Immediate;
 				rp.InMemory = true;
@@ -315,13 +314,16 @@ namespace WsProxy {
 				this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
 			} catch (BadImageFormatException ex) {
 				Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}");
+			} catch (ArgumentNullException) {
+				if (pdb != null)
+					throw;
 			}
 
 			if (this.image == null) {
 				ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
 				if (pdb != null) {
 					rp.ReadSymbols = true;
-					rp.SymbolReaderProvider = new NativePdbReaderProvider ();
+					rp.SymbolReaderProvider = new PdbReaderProvider ();
 					rp.SymbolStream = new MemoryStream (pdb);
 				}
 
@@ -491,61 +493,72 @@ namespace WsProxy {
 	}
 
 	internal class DebugStore {
+        // MonoProxy proxy;  - commenting out because never gets assigned
 		List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
+		HttpClient client = new HttpClient ();
+
+		class DebugItem {
+			public string Url { get; set; }
+			public Task<byte[][]> Data { get; set; }
+		}
 
-		public DebugStore (string [] loaded_files)
+		public async Task Load (SessionId sessionId, string [] loaded_files, CancellationToken token)
 		{
-			bool MatchPdb (string asm, string pdb)
-			{
-				return Path.ChangeExtension (asm, "pdb") == pdb;
-			}
+			static bool MatchPdb (string asm, string pdb)
+				=> Path.ChangeExtension (asm, "pdb") == pdb;
 
 			var asm_files = new List<string> ();
 			var pdb_files = new List<string> ();
-			foreach (var f in loaded_files) {
-				var file_name = f;
+			foreach (var file_name in loaded_files) {
 				if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase))
 					pdb_files.Add (file_name);
 				else
 					asm_files.Add (file_name);
 			}
 
-			//FIXME make this parallel
-			foreach (var p in asm_files) {
+			List<DebugItem> steps = new List<DebugItem> ();
+			foreach (var url in asm_files) {
 				try {
-					var pdb = pdb_files.FirstOrDefault (n => MatchPdb (p, n));
-					HttpClient h = new HttpClient ();
-					var assembly_bytes = h.GetByteArrayAsync (p).Result;
-					byte [] pdb_bytes = null;
-					if (pdb != null)
-						pdb_bytes = h.GetByteArrayAsync (pdb).Result;
-
-					this.assemblies.Add (new AssemblyInfo (assembly_bytes, pdb_bytes));
+					var pdb = pdb_files.FirstOrDefault (n => MatchPdb (url, n));
+					steps.Add (
+						new DebugItem {
+								Url = url,
+								Data = Task.WhenAll (client.GetByteArrayAsync (url), pdb != null ? client.GetByteArrayAsync (pdb) : Task.FromResult<byte []> (null))
+						});
 				} catch (Exception e) {
-					Console.WriteLine ($"Failed to read {p} ({e.Message})");
+					Console.WriteLine ($"Failed to read {url} ({e.Message})");
+					var o = JObject.FromObject (new {
+						entry = new {
+							source = "other",
+							level = "warning",
+							text = $"Failed to read {url} ({e.Message})"
+						}
+					});
+					// proxy.SendEvent (sessionId, "Log.entryAdded", o, token); - commenting out because `proxy` would always be null
+
 				}
 			}
-		}
 
-		public IEnumerable<SourceFile> AllSources ()
-		{
-			foreach (var a in assemblies) {
-				foreach (var s in a.Sources)
-					yield return s;
+			foreach (var step in steps) {
+				try {
+					var bytes = await step.Data;
+					assemblies.Add (new AssemblyInfo (step.Url, bytes[0], bytes[1]));
+				} catch (Exception e) {
+					Console.WriteLine ($"Failed to Load {step.Url} ({e.Message})");
+				}
 			}
 		}
 
+		public IEnumerable<SourceFile> AllSources ()
+			=> assemblies.SelectMany (a => a.Sources);
+
 		public SourceFile GetFileById (SourceId id)
-		{
-			return AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
-		}
+			=> AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
 
 		public AssemblyInfo GetAssemblyByName (string name)
-		{
-			return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
-		}
+			=> assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
 
-		/*	
+		/*
 		V8 uses zero based indexing for both line and column.
 		PPDBs uses one based indexing for both line and column.
 		*/
@@ -598,7 +611,7 @@ namespace WsProxy {
 		PPDBs uses one based indexing for both line and column.
 		*/
 		static bool Match (SequencePoint sp, int line, int column)
-		{ 
+		{
 			var bp = (line: line + 1, column: column + 1);
 
 			if (sp.StartLine > bp.line || sp.EndLine < bp.line)
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3fa95ef3463a6d809ac56c5dfb3f30178f3cede4
--- /dev/null
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Threading.Tasks;
+
+using System.Net.WebSockets;
+using System.Threading;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace WebAssembly.Net.Debugging {
+	internal class DevToolsClient: IDisposable {
+		ClientWebSocket socket;
+		List<Task> pending_ops = new List<Task> ();
+		TaskCompletionSource<bool> side_exit = new TaskCompletionSource<bool> ();
+		List<byte []> pending_writes = new List<byte []> ();
+		Task current_write;
+
+		public DevToolsClient () {
+		}
+
+		~DevToolsClient() {
+			Dispose(false);
+		}
+
+		public void Dispose() {
+			Dispose(true);
+		}
+
+		public async Task Close (CancellationToken cancellationToken)
+		{
+			if (socket.State == WebSocketState.Open)
+				await socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
+		}
+
+		protected virtual void Dispose (bool disposing) {
+			if (disposing)
+				socket.Dispose ();
+		}
+
+		Task Pump (Task task, CancellationToken token)
+		{
+			if (task != current_write)
+				return null;
+			current_write = null;
+
+			pending_writes.RemoveAt (0);
+
+			if (pending_writes.Count > 0) {
+				current_write = socket.SendAsync (new ArraySegment<byte> (pending_writes [0]), WebSocketMessageType.Text, true, token);
+				return current_write;
+			}
+			return null;
+		}
+
+		async Task<string> ReadOne (CancellationToken token)
+		{
+			byte [] buff = new byte [4000];
+			var mem = new MemoryStream ();
+			while (true) {
+				var result = await this.socket.ReceiveAsync (new ArraySegment<byte> (buff), token);
+				if (result.MessageType == WebSocketMessageType.Close) {
+					return null;
+				}
+
+				if (result.EndOfMessage) {
+					mem.Write (buff, 0, result.Count);
+					return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
+				} else {
+					mem.Write (buff, 0, result.Count);
+				}
+			}
+		}
+
+		protected void Send (byte [] bytes, CancellationToken token)
+		{
+			pending_writes.Add (bytes);
+			if (pending_writes.Count == 1) {
+				if (current_write != null)
+					throw new Exception ("Internal state is bad. current_write must be null if there are no pending writes");
+
+				current_write = socket.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
+				pending_ops.Add (current_write);
+			}
+		}
+
+		async Task MarkCompleteAfterward (Func<CancellationToken, Task> send, CancellationToken token)
+		{
+			try {
+				await send(token);
+				side_exit.SetResult (true);
+			} catch (Exception e) {
+				side_exit.SetException (e);
+			}
+		}
+
+		protected async Task<bool> ConnectWithMainLoops(
+			Uri uri,
+			Func<string, CancellationToken, Task> receive,
+			Func<CancellationToken, Task> send,
+			CancellationToken token) {
+
+			Console.WriteLine ("connecting to {0}", uri);
+			this.socket = new ClientWebSocket ();
+			this.socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
+
+			await this.socket.ConnectAsync (uri, token);
+			pending_ops.Add (ReadOne (token));
+			pending_ops.Add (side_exit.Task);
+			pending_ops.Add (MarkCompleteAfterward (send, token));
+
+			while (!token.IsCancellationRequested) {
+				var task = await Task.WhenAny (pending_ops);
+				if (task == pending_ops [0]) { //pending_ops[0] is for message reading
+					var msg = ((Task<string>)task).Result;
+					pending_ops [0] = ReadOne (token);
+					Task tsk = receive (msg, token);
+					if (tsk != null)
+						pending_ops.Add (tsk);
+				} else if (task == pending_ops [1]) {
+					var res = ((Task<bool>)task).Result;
+					//it might not throw if exiting successfull
+					return res;
+				} else { //must be a background task
+					pending_ops.Remove (task);
+					var tsk = Pump (task, token);
+					if (tsk != null)
+						pending_ops.Add (tsk);
+				}
+			}
+
+			return false;
+		}
+
+		protected virtual void Log (string priority, string msg)
+		{
+			//
+		}
+	}
+}
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs
similarity index 57%
rename from src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs
rename to src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs
index 87ef23027e8a0fd86535aacf16bfd5708b696321..e2e24eff79429158e0d25d045e4fea2a692dfbc6 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs
@@ -8,8 +8,16 @@ using System.Threading;
 using System.IO;
 using System.Text;
 using System.Collections.Generic;
+using Microsoft.Extensions.Logging;
 
-namespace WsProxy {
+namespace WebAssembly.Net.Debugging {
+	internal class SessionId {
+		public string sessionId;
+	}
+
+	internal class MessageId : SessionId {
+		public int id;
+	}
 
 	internal struct Result {
 		public JObject Value { get; private set; }
@@ -26,6 +34,7 @@ namespace WsProxy {
 
 		public static Result FromJson (JObject obj)
 		{
+			//Log ("protocol", $"from result: {obj}");
 			return new Result (obj ["result"] as JObject, obj ["error"] as JObject);
 		}
 
@@ -39,28 +48,30 @@ namespace WsProxy {
 			return new Result (null, err);
 		}
 
-		public JObject ToJObject (int id) {
+		public JObject ToJObject (MessageId target) {
 			if (IsOk) {
 				return JObject.FromObject (new {
-					id = id,
+					target.id,
+					target.sessionId,
 					result = Value
 				});
 			} else {
 				return JObject.FromObject (new {
-					id = id,
+					target.id,
+					target.sessionId,
 					error = Error
 				});
 			}
 		}
 	}
 
-	class WsQueue {
+	class DevToolsQueue {
 		Task current_send;
 		List<byte []> pending;
 
 		public WebSocket Ws { get; private set; }
 		public Task CurrentSend { get { return current_send; } }
-		public WsQueue (WebSocket sock)
+		public DevToolsQueue (WebSocket sock)
 		{
 			this.Ws = sock;
 			pending = new List<byte []> ();
@@ -71,7 +82,7 @@ namespace WsProxy {
 			pending.Add (bytes);
 			if (pending.Count == 1) {
 				if (current_send != null)
-					throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send");
+					throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
 				//Console.WriteLine ("sending {0} bytes", bytes.Length);
 				current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
 				return current_send;
@@ -86,7 +97,7 @@ namespace WsProxy {
 
 			if (pending.Count > 0) {
 				if (current_send != null)
-					throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send");
+					throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
 				//Console.WriteLine ("sending more {0} bytes", pending[0].Length);
 				current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token);
 				return current_send;
@@ -95,22 +106,28 @@ namespace WsProxy {
 		}
 	}
 
-	internal class WsProxy {
+	internal class DevToolsProxy {
 		TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> ();
 		TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> ();
-		List<(int, TaskCompletionSource<Result>)> pending_cmds = new List<(int, TaskCompletionSource<Result>)> ();
+		List<(MessageId, TaskCompletionSource<Result>)> pending_cmds = new List<(MessageId, TaskCompletionSource<Result>)> ();
 		ClientWebSocket browser;
 		WebSocket ide;
 		int next_cmd_id;
 		List<Task> pending_ops = new List<Task> ();
-		List<WsQueue> queues = new List<WsQueue> ();
+		List<DevToolsQueue> queues = new List<DevToolsQueue> ();
+        readonly ILogger logger;
+
+        public DevToolsProxy(ILoggerFactory loggerFactory)
+        {
+            logger = loggerFactory.CreateLogger<DevToolsProxy>();
+        }
 
-		protected virtual Task<bool> AcceptEvent (string method, JObject args, CancellationToken token)
+		protected virtual Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
 			return Task.FromResult (false);
 		}
 
-		protected virtual Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token)
+		protected virtual Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
 		{
 			return Task.FromResult (false);
 		}
@@ -122,7 +139,7 @@ namespace WsProxy {
 			while (true) {
 
 				if (socket.State != WebSocketState.Open) {
-					Console.WriteLine ($"WSProxy: Socket is no longer open.");
+					Log ("error", $"DevToolsProxy: Socket is no longer open.");
 					client_initiated_close.TrySetResult (true);
 					return null;
 				}
@@ -133,51 +150,53 @@ namespace WsProxy {
 					return null;
 				}
 
-				if (result.EndOfMessage) {
-					mem.Write (buff, 0, result.Count);
+				mem.Write (buff, 0, result.Count);
+
+				if (result.EndOfMessage)
 					return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
-				} else {
-					mem.Write (buff, 0, result.Count);
-				}
 			}
 		}
 
-		WsQueue GetQueueForSocket (WebSocket ws)
+		DevToolsQueue GetQueueForSocket (WebSocket ws)
 		{
 			return queues.FirstOrDefault (q => q.Ws == ws);
 		}
 
-		WsQueue GetQueueForTask (Task task) {
+		DevToolsQueue GetQueueForTask (Task task)
+		{
 			return queues.FirstOrDefault (q => q.CurrentSend == task);
 		}
 
 		void Send (WebSocket to, JObject o, CancellationToken token)
 		{
-			var bytes = Encoding.UTF8.GetBytes (o.ToString ());		
+			var sender = browser == to ? "Send-browser" : "Send-ide";
+			Log ("protocol", $"{sender}: {o}");
+			var bytes = Encoding.UTF8.GetBytes (o.ToString ());
 
 			var queue = GetQueueForSocket (to);
+
 			var task = queue.Send (bytes, token);
 			if (task != null)
 				pending_ops.Add (task);
 		}
 
-		async Task OnEvent (string method, JObject args, CancellationToken token)
+		async Task OnEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
 			try {
-				if (!await AcceptEvent (method, args, token)) {
+				if (!await AcceptEvent (sessionId, method, args, token)) {
 					//Console.WriteLine ("proxy browser: {0}::{1}",method, args);
-					SendEventInternal (method, args, token);
+					SendEventInternal (sessionId, method, args, token);
 				}
 			} catch (Exception e) {
 				side_exception.TrySetException (e);
 			}
 		}
 
-		async Task OnCommand (int id, string method, JObject args, CancellationToken token)
+		async Task OnCommand (MessageId id, string method, JObject args, CancellationToken token)
 		{
 			try {
 				if (!await AcceptCommand (id, method, args, token)) {
-					var res = await SendCommandInternal (method, args, token);
+					var res = await SendCommandInternal (id, method, args, token);
 					SendResponseInternal (id, res, token);
 				}
 			} catch (Exception e) {
@@ -185,10 +204,11 @@ namespace WsProxy {
 			}
 		}
 
-		void OnResponse (int id, Result result)
+		void OnResponse (MessageId id, Result result)
 		{
 			//Console.WriteLine ("got id {0} res {1}", id, result);
-			var idx = pending_cmds.FindIndex (e => e.Item1 == id);
+			// Fixme
+			var idx = pending_cmds.FindIndex (e => e.Item1.id == id.id && e.Item1.sessionId == id.sessionId);
 			var item = pending_cmds [idx];
 			pending_cmds.RemoveAt (idx);
 
@@ -197,68 +217,74 @@ namespace WsProxy {
 
 		void ProcessBrowserMessage (string msg, CancellationToken token)
 		{
-			// Debug ($"browser: {msg}");
+			Log ("protocol", $"browser: {msg}");
 			var res = JObject.Parse (msg);
 
 			if (res ["id"] == null)
-				pending_ops.Add (OnEvent (res ["method"].Value<string> (), res ["params"] as JObject, token));
+				pending_ops.Add (OnEvent (new SessionId { sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
 			else
-				OnResponse (res ["id"].Value<int> (), Result.FromJson (res));
+				OnResponse (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, Result.FromJson (res));
 		}
 
 		void ProcessIdeMessage (string msg, CancellationToken token)
 		{
+			Log ("protocol", $"ide: {msg}");
 			if (!string.IsNullOrEmpty (msg)) {
 				var res = JObject.Parse (msg);
-				pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token));
+				pending_ops.Add (OnCommand (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
 			}
 		}
 
-		internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
-			// Debug ($"sending command {method}: {args}");
-			return await SendCommandInternal (method, args, token);
+		internal async Task<Result> SendCommand (SessionId id, string method, JObject args, CancellationToken token) {
+			//Log ("verbose", $"sending command {method}: {args}");
+			return await SendCommandInternal (id, method, args, token);
 		}
 
-		Task<Result> SendCommandInternal (string method, JObject args, CancellationToken token)
+		Task<Result> SendCommandInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
 			int id = ++next_cmd_id;
 
 			var o = JObject.FromObject (new {
-				id = id,
-				method = method,
+				sessionId.sessionId,
+				id,
+				method,
 				@params = args
 			});
 			var tcs = new TaskCompletionSource<Result> ();
-			//Console.WriteLine ("add cmd id {0}", id);
-			pending_cmds.Add ((id, tcs));
+
+
+			var msgId = new MessageId { id = id, sessionId = sessionId.sessionId };
+			//Log ("verbose", $"add cmd id {sessionId}-{id}");
+			pending_cmds.Add ((msgId , tcs));
 
 			Send (this.browser, o, token);
 			return tcs.Task;
 		}
 
-		public void SendEvent (string method, JObject args, CancellationToken token)
+		public void SendEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
-			//Debug ($"sending event {method}: {args}");
-			SendEventInternal (method, args, token);
+			//Log ("verbose", $"sending event {method}: {args}");
+			SendEventInternal (sessionId, method, args, token);
 		}
 
-		void SendEventInternal (string method, JObject args, CancellationToken token)
+		void SendEventInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
 			var o = JObject.FromObject (new {
-				method = method,
+				sessionId.sessionId,
+				method,
 				@params = args
 			});
 
 			Send (this.ide, o, token);
 		}
 
-		internal void SendResponse (int id, Result result, CancellationToken token)
+		internal void SendResponse (MessageId id, Result result, CancellationToken token)
 		{
-			//Debug ($"sending response: {id}: {result.ToJObject (id)}");
+			//Log ("verbose", $"sending response: {id}: {result.ToJObject (id)}");
 			SendResponseInternal (id, result, token);
 		}
 
-		void SendResponseInternal (int id, Result result, CancellationToken token)
+		void SendResponseInternal (MessageId id, Result result, CancellationToken token)
 		{
 			JObject o = result.ToJObject (id);
 
@@ -268,16 +294,16 @@ namespace WsProxy {
 		// , HttpContext context)
 		public async Task Run (Uri browserUri, WebSocket ideSocket)
 		{
-			Debug ($"WsProxy Starting on {browserUri}");
+			Log ("info", $"DevToolsProxy: Starting on {browserUri}");
 			using (this.ide = ideSocket) {
-				Debug ($"WsProxy: IDE waiting for connection on {browserUri}");
-				queues.Add (new WsQueue (this.ide));
+				Log ("verbose", $"DevToolsProxy: IDE waiting for connection on {browserUri}");
+				queues.Add (new DevToolsQueue (this.ide));
 				using (this.browser = new ClientWebSocket ()) {
 					this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
 					await this.browser.ConnectAsync (browserUri, CancellationToken.None);
-					queues.Add (new WsQueue (this.browser));
+					queues.Add (new DevToolsQueue (this.browser));
 
-					Debug ($"WsProxy: Client connected on {browserUri}");
+					Log ("verbose", $"DevToolsProxy: Client connected on {browserUri}");
 					var x = new CancellationTokenSource ();
 
 					pending_ops.Add (ReadOne (browser, x.Token));
@@ -306,7 +332,7 @@ namespace WsProxy {
 								throw new Exception ("side task must always complete with an exception, what's going on???");
 							} else if (task == pending_ops [3]) {
 								var res = ((Task<bool>)task).Result;
-								Debug ($"WsProxy: Client initiated close from {browserUri}");
+								Log ("verbose", $"DevToolsProxy: Client initiated close from {browserUri}");
 								x.Cancel ();
 							} else {
 								//must be a background task
@@ -320,7 +346,7 @@ namespace WsProxy {
 							}
 						}
 					} catch (Exception e) {
-						Debug ($"WsProxy::Run: Exception {e}");
+						Log ("error", $"DevToolsProxy::Run: Exception {e}");
 						//throw;
 					} finally {
 						if (!x.IsCancellationRequested)
@@ -330,14 +356,28 @@ namespace WsProxy {
 			}
 		}
 
-		protected void Debug (string msg)
+		protected void Log (string priority, string msg)
 		{
-			Console.WriteLine (msg);
-		}
-
-		protected void Info (string msg)
-		{
-			Console.WriteLine (msg);
+			switch (priority) {
+			case "protocol":
+                logger.LogTrace (msg);
+				break;
+			case "verbose":
+				logger.LogDebug (msg);
+				break;
+			case "info":
+                logger.LogInformation(msg);
+                break;
+			case "warning":
+                logger.LogWarning(msg);
+                break;
+            case "error":
+                logger.LogError (msg);
+                break;
+			default:
+                logger.LogError(msg);
+                break;
+			}
 		}
 	}
 }
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
index eb4cf65b50a41b54e41375e0939b4b104a70ca29..e74d4684948618bb0661a5510a10c20ff809fd92 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
@@ -3,14 +3,13 @@ using System.Linq;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;
 
-using System.Net.WebSockets;
 using System.Threading;
 using System.IO;
-using System.Text;
 using System.Collections.Generic;
 using System.Net;
+using Microsoft.Extensions.Logging;
 
-namespace WsProxy {
+namespace WebAssembly.Net.Debugging {
 
 	internal class MonoCommands {
 		public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()";
@@ -29,10 +28,10 @@ namespace WsProxy {
 		BpNotFound = 100000,
 	}
 
-
 	internal class MonoConstants {
 		public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
 	}
+
 	class Frame {
 		public Frame (MethodInfo method, SourceLocation location, int id)
 		{
@@ -73,7 +72,7 @@ namespace WsProxy {
 		Over
 	}
 
-	internal class MonoProxy : WsProxy {
+	internal class MonoProxy : DevToolsProxy {
 		DebugStore store;
 		List<Breakpoint> breakpoints = new List<Breakpoint> ();
 		List<Frame> current_callstack;
@@ -82,9 +81,9 @@ namespace WsProxy {
 		int ctx_id;
 		JObject aux_ctx_data;
 
-		public MonoProxy () { }
+		public MonoProxy (ILoggerFactory loggerFactory) : base(loggerFactory) { }
 
-		protected override async Task<bool> AcceptEvent (string method, JObject args, CancellationToken token)
+		protected override async Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
 		{
 			switch (method) {
 			case "Runtime.executionContextCreated": {
@@ -93,8 +92,8 @@ namespace WsProxy {
 					if (aux_data != null) {
 						var is_default = aux_data ["isDefault"]?.Value<bool> ();
 						if (is_default == true) {
-							var ctx_id = ctx ["id"].Value<int> ();
-							await OnDefaultContext (ctx_id, aux_data, token);
+							var id = new MessageId { id = ctx ["id"].Value<int> (), sessionId = sessionId.sessionId };
+							await OnDefaultContext (id, aux_data, token);
 						}
 					}
 					break;
@@ -103,11 +102,11 @@ namespace WsProxy {
 					//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
 					var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> ();
 					if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") {
-						await OnBreakPointHit (args, token);
+						await OnBreakPointHit (sessionId, args, token);
 						return true;
 					}
 					if (top_func == MonoConstants.RUNTIME_IS_READY) {
-						await OnRuntimeReady (token);
+						await OnRuntimeReady (new SessionId { sessionId = sessionId.sessionId }, token);
 						return true;
 					}
 					break;
@@ -119,24 +118,33 @@ namespace WsProxy {
 					}
 					break;
 				}
+			case "Debugger.enabled": {
+					await LoadStore (new SessionId { sessionId = args? ["sessionId"]?.Value<string> () }, token);
+					break;
+				}
 			}
-
 			return false;
 		}
 
 
-		protected override async Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token)
+		protected override async Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
 		{
 			switch (method) {
+			case "Target.attachToTarget": {
+					break;
+				}
+			case "Target.attachToBrowserTarget": {
+					break;
+				}
 			case "Debugger.getScriptSource": {
 					var script_id = args? ["scriptId"]?.Value<string> ();
 					if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) {
 						await OnGetScriptSource (id, script_id, token);
 						return true;
 					}
-
 					break;
 				}
+
 			case "Runtime.compileScript": {
 					var exp = args? ["expression"]?.Value<string> ();
 					if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) {
@@ -156,7 +164,7 @@ namespace WsProxy {
 				}
 
 			case "Debugger.setBreakpointByUrl": {
-					Info ($"BP req {args}");
+					Log ("info", $"BP req {args}");
 					var bp_req = BreakPointRequest.Parse (args, store);
 					if (bp_req != null) {
 						await SetBreakPoint (id, bp_req, token);
@@ -164,9 +172,10 @@ namespace WsProxy {
 					}
 					break;
 				}
+
 			case "Debugger.removeBreakpoint": {
-				return await RemoveBreakpoint (id, args, token);
-			}
+					return await RemoveBreakpoint (id, args, token);
+				}
 
 			case "Debugger.resume": {
 					await OnResume (token);
@@ -199,16 +208,24 @@ namespace WsProxy {
 
 			case "Runtime.getProperties": {
 					var objId = args? ["objectId"]?.Value<string> ();
-					if (objId.StartsWith ("dotnet:scope:", StringComparison.InvariantCulture)) {
-						await GetScopeProperties (id, int.Parse (objId.Substring ("dotnet:scope:".Length)), token);
-						return true;
-					}
-					if (objId.StartsWith("dotnet:", StringComparison.InvariantCulture))
-					{
-						if (objId.StartsWith("dotnet:object:", StringComparison.InvariantCulture))
-							await GetDetails(id, int.Parse(objId.Substring("dotnet:object:".Length)), token, MonoCommands.GET_OBJECT_PROPERTIES);
-						if (objId.StartsWith("dotnet:array:", StringComparison.InvariantCulture))
-							await GetDetails(id, int.Parse(objId.Substring("dotnet:array:".Length)), token, MonoCommands.GET_ARRAY_VALUES);
+					if (objId.StartsWith ("dotnet:")) {
+						var parts = objId.Split (new char [] { ':' });
+						if (parts.Length < 3)
+							return true;
+						switch (parts[1]) {
+						case "scope": {
+							await GetScopeProperties (id, int.Parse (parts[2]), token);
+							break;
+							}
+						case "object": {
+							await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_OBJECT_PROPERTIES);
+							break;
+							}
+						case "array": {
+							await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_ARRAY_VALUES);
+							break;
+							}
+						}
 						return true;
 					}
 					break;
@@ -218,15 +235,16 @@ namespace WsProxy {
 			return false;
 		}
 
-		async Task OnRuntimeReady (CancellationToken token)
+		async Task OnRuntimeReady (SessionId sessionId, CancellationToken token)
 		{
-			Info ("RUNTIME READY, PARTY TIME");
-			await RuntimeReady (token);
-			await SendCommand ("Debugger.resume", new JObject (), token);
-			SendEvent ("Mono.runtimeReady", new JObject (), token);			
+			Log ("info", "RUNTIME READY, PARTY TIME");
+			await RuntimeReady (sessionId, token);
+			await SendCommand (sessionId, "Debugger.resume", new JObject (), token);
+			SendEvent (sessionId, "Mono.runtimeReady", new JObject (), token);
 		}
 
-		async Task OnBreakPointHit (JObject args, CancellationToken token)
+		//static int frame_id=0;
+		async Task OnBreakPointHit (SessionId sessionId, JObject args, CancellationToken token)
 		{
 			//FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
 			var o = JObject.FromObject (new {
@@ -238,11 +256,11 @@ namespace WsProxy {
 			});
 
 			var orig_callframes = args? ["callFrames"]?.Values<JObject> ();
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
 
 			if (res.IsErr) {
 				//Give up and send the original call stack
-				SendEvent ("Debugger.paused", args, token);
+				SendEvent (sessionId, "Debugger.paused", args, token);
 				return;
 			}
 
@@ -250,16 +268,16 @@ namespace WsProxy {
 			var res_value = res.Value? ["result"]? ["value"];
 			if (res_value == null || res_value is JValue) {
 				//Give up and send the original call stack
-				SendEvent ("Debugger.paused", args, token);
+				SendEvent (sessionId, "Debugger.paused", args, token);
 				return;
 			}
 
-			Debug ($"call stack (err is {res.Error} value is:\n{res.Value}");
+			Log ("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}");
 			var bp_id = res_value? ["breakpoint_id"]?.Value<int> ();
-			Debug ($"We just hit bp {bp_id}");
+			Log ("verbose", $"We just hit bp {bp_id}");
 			if (!bp_id.HasValue) {
 				//Give up and send the original call stack
-				SendEvent ("Debugger.paused", args, token);
+				SendEvent (sessionId, "Debugger.paused", args, token);
 				return;
 			}
 			var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value);
@@ -282,14 +300,14 @@ namespace WsProxy {
 
 						var asm = store.GetAssemblyByName (assembly_name);
 						if (asm == null) {
-							Info ($"Unable to find assembly: {assembly_name}");
+							Log ("info",$"Unable to find assembly: {assembly_name}");
 							continue;
 						}
 
 						var method = asm.GetMethodByToken (method_token);
 
 						if (method == null) {
-							Info ($"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
+							Log ("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
 							continue;
 						}
 
@@ -303,8 +321,8 @@ namespace WsProxy {
 							continue;
 						}
 
-						Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
-						Info ($"\tmethod {method.Name} location: {location}");
+						Log ("info", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
+						Log ("info", $"\tmethod {method.Name} location: {location}");
 						frames.Add (new Frame (method, location, frame_id));
 
 						callFrames.Add (JObject.FromObject (new {
@@ -351,12 +369,12 @@ namespace WsProxy {
 				hitBreakpoints = bp_list,
 			});
 
-			SendEvent ("Debugger.paused", o, token);
+			SendEvent (sessionId, "Debugger.paused", o, token);
 		}
 
-		async Task OnDefaultContext (int ctx_id, JObject aux_data, CancellationToken token)
+		async Task OnDefaultContext (MessageId ctx_id, JObject aux_data, CancellationToken token)
 		{
-			Debug ("Default context created, clearing state and sending events");
+			Log ("verbose", "Default context created, clearing state and sending events");
 
 			//reset all bps
 			foreach (var b in this.breakpoints){
@@ -371,16 +389,16 @@ namespace WsProxy {
 				silent = false,
 				returnByValue = true
 			});
-			this.ctx_id = ctx_id;
+			this.ctx_id = ctx_id.id;
 			this.aux_ctx_data = aux_data;
 
-			Debug ("checking if the runtime is ready");
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			Log ("verbose", "checking if the runtime is ready");
+			var res = await SendCommand (ctx_id, "Runtime.evaluate", o, token);
 			var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> ();
-			//Debug ($"\t{is_ready}");
+			//Log ("verbose", $"\t{is_ready}");
 			if (is_ready.HasValue && is_ready.Value == true) {
-				Debug ("RUNTIME LOOK READY. GO TIME!");
-				await OnRuntimeReady (token);
+				Log ("verbose", "RUNTIME LOOK READY. GO TIME!");
+				await OnRuntimeReady (ctx_id, token);
 			}
 		}
 
@@ -392,7 +410,7 @@ namespace WsProxy {
 			await Task.CompletedTask;
 		}
 
-		async Task Step (int msg_id, StepKind kind, CancellationToken token)
+		async Task Step (MessageId msg_id, StepKind kind, CancellationToken token)
 		{
 
 			var o = JObject.FromObject (new {
@@ -403,16 +421,16 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
 
 			SendResponse (msg_id, Result.Ok (new JObject ()), token);
 
 			this.current_callstack = null;
 
-			await SendCommand ("Debugger.resume", new JObject (), token);
+			await SendCommand (msg_id, "Debugger.resume", new JObject (), token);
 		}
 
-		async Task GetDetails(int msg_id, int object_id, CancellationToken token, string command)
+		async Task GetDetails(MessageId msg_id, int object_id, CancellationToken token, string command)
 		{
 			var o = JObject.FromObject(new
 			{
@@ -423,7 +441,7 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var res = await SendCommand("Runtime.evaluate", o, token);
+			var res = await SendCommand(msg_id, "Runtime.evaluate", o, token);
 
 			//if we fail we just buble that to the IDE (and let it panic over it)
 			if (res.IsErr)
@@ -461,14 +479,13 @@ namespace WsProxy {
 				{
 					result = var_list
 				});
-			} catch (Exception) {
-				Debug ($"failed to parse {res.Value}");
+			} catch (Exception e) {
+				Log ("verbose", $"failed to parse {res.Value} - {e.Message}");
 			}
 			SendResponse(msg_id, Result.Ok(o), token);
 		}
 
-
-		async Task GetScopeProperties (int msg_id, int scope_id, CancellationToken token)
+		async Task GetScopeProperties (MessageId msg_id, int scope_id, CancellationToken token)
 		{
 			var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id);
 			var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset);
@@ -484,7 +501,7 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
 
 			//if we fail we just buble that to the IDE (and let it panic over it)
 			if (res.IsErr) {
@@ -533,13 +550,13 @@ namespace WsProxy {
 					result = var_list
 				});
 				SendResponse (msg_id, Result.Ok (o), token);
-			}
-			catch (Exception) {
+			} catch (Exception exception) {
+				Log ("verbose", $"Error resolving scope properties {exception.Message}");
 				SendResponse (msg_id, res, token);
 			}
 		}
 
-		async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)
+		async Task<Result> EnableBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
 		{
 			var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
 			var method_token = bp.Location.CliLocation.Method.Token;
@@ -553,21 +570,20 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
 			var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
 
 			if (ret_code.HasValue) {
 				bp.RemoteId = ret_code.Value;
 				bp.State = BreakPointState.Active;
-				//Debug ($"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
+				//Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
 			}
 
 			return res;
 		}
 
-		async Task RuntimeReady (CancellationToken token)
+		async Task LoadStore (SessionId sessionId, CancellationToken token)
 		{
-
 			var o = JObject.FromObject (new {
 				expression = MonoCommands.GET_LOADED_FILES,
 				objectGroup = "mono_debugger",
@@ -575,10 +591,19 @@ namespace WsProxy {
 				silent = false,
 				returnByValue = true,
 			});
-			var loaded_pdbs = await SendCommand ("Runtime.evaluate", o, token);
+
+			var loaded_pdbs = await SendCommand (sessionId, "Runtime.evaluate", o, token);
 			var the_value = loaded_pdbs.Value? ["result"]? ["value"];
 			var the_pdbs = the_value?.ToObject<string[]> ();
-			this.store = new DebugStore (the_pdbs);
+
+			store = new DebugStore ();
+			await store.Load(sessionId, the_pdbs, token);
+		}
+
+		async Task RuntimeReady (SessionId sessionId, CancellationToken token)
+		{
+			if (store == null)
+				await LoadStore (sessionId, token);
 
 			foreach (var s in store.AllSources ()) {
 				var ok = JObject.FromObject (new {
@@ -587,13 +612,13 @@ namespace WsProxy {
 					executionContextId = this.ctx_id,
 					hash = s.DocHashCode,
 					executionContextAuxData = this.aux_ctx_data,
-					dotNetUrl = s.DotNetUrl
+					dotNetUrl = s.DotNetUrl,
 				});
-				//Debug ($"\tsending {s.Url}");
-				SendEvent ("Debugger.scriptParsed", ok, token);
+				//Log ("verbose", $"\tsending {s.Url}");
+				SendEvent (sessionId, "Debugger.scriptParsed", ok, token);
 			}
 
-			o = JObject.FromObject (new {
+			var o = JObject.FromObject (new {
 				expression = MonoCommands.CLEAR_ALL_BREAKPOINTS,
 				objectGroup = "mono_debugger",
 				includeCommandLineAPI = false,
@@ -601,9 +626,9 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var clear_result = await SendCommand ("Runtime.evaluate", o, token);
+			var clear_result = await SendCommand (sessionId, "Runtime.evaluate", o, token);
 			if (clear_result.IsErr) {
-				Debug ($"Failed to clear breakpoints due to {clear_result}");
+				Log ("verbose", $"Failed to clear breakpoints due to {clear_result}");
 			}
 
 
@@ -612,19 +637,19 @@ namespace WsProxy {
 			foreach (var bp in breakpoints) {
 				if (bp.State != BreakPointState.Pending)
 					continue;
-				var res = await EnableBreakPoint (bp, token);
+				var res = await EnableBreakPoint (sessionId, bp, token);
 				var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
 
 				//if we fail we just buble that to the IDE (and let it panic over it)
 				if (!ret_code.HasValue) {
 					//FIXME figure out how to inform the IDE of that.
-					Info ($"FAILED TO ENABLE BP {bp.LocalId}");
+					Log ("info", $"FAILED TO ENABLE BP {bp.LocalId}");
 					bp.State = BreakPointState.Disabled;
 				}
 			}
 		}
 
-		async Task<bool> RemoveBreakpoint(int msg_id, JObject args, CancellationToken token) {
+		async Task<bool> RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
 			var bpid = args? ["breakpointId"]?.Value<string> ();
 			if (bpid?.StartsWith ("dotnet:") != true)
 				return false;
@@ -633,19 +658,19 @@ namespace WsProxy {
 
 			var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id);
 			if (bp == null) {
-				Info ($"Could not find dotnet bp with id {the_id}");
+				Log ("info", $"Could not find dotnet bp with id {the_id}");
 				return false;
 			}
 
 			breakpoints.Remove (bp);
 			//FIXME verify result (and log?)
-			var res = await RemoveBreakPoint (bp, token);
+			var res = await RemoveBreakPoint (msg_id, bp, token);
 
 			return true;
 		}
 
 
-		async Task<Result> RemoveBreakPoint (Breakpoint bp, CancellationToken token)
+		async Task<Result> RemoveBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
 		{
 			var o = JObject.FromObject (new {
 				expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId),
@@ -655,7 +680,7 @@ namespace WsProxy {
 				returnByValue = true,
 			});
 
-			var res = await SendCommand ("Runtime.evaluate", o, token);
+			var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
 			var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
 
 			if (ret_code.HasValue) {
@@ -666,13 +691,13 @@ namespace WsProxy {
 			return res;
 		}
 
-		async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token)
+		async Task SetBreakPoint (MessageId msg_id, BreakPointRequest req, CancellationToken token)
 		{
-			var bp_loc = store.FindBestBreakpoint (req);
-			Info ($"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
+			var bp_loc = store?.FindBestBreakpoint (req);
+			Log ("info", $"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
 			if (bp_loc == null) {
 
-				Info ($"Could not resolve breakpoint request: {req}");
+				Log ("info", $"Could not resolve breakpoint request: {req}");
 				SendResponse (msg_id, Result.Err(JObject.FromObject (new {
 					code = (int)MonoErrorCodes.BpNotFound,
 					message = $"C# Breakpoint at {req} not found."
@@ -686,7 +711,7 @@ namespace WsProxy {
 			} else {
 				bp = new Breakpoint (bp_loc, local_breakpoint_id++, BreakPointState.Disabled);
 
-				var res = await EnableBreakPoint (bp, token);
+				var res = await EnableBreakPoint (msg_id, bp, token);
 				var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
 
 				//if we fail we just buble that to the IDE (and let it panic over it)
@@ -714,7 +739,7 @@ namespace WsProxy {
 			SendResponse (msg_id, Result.Ok (ok), token);
 		}
 
-		bool GetPossibleBreakpoints (int msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
+		bool GetPossibleBreakpoints (MessageId msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
 		{
 			var bps = store.FindPossibleBreakpoints (start, end);
 			if (bps == null)
@@ -734,15 +759,14 @@ namespace WsProxy {
 			return true;
 		}
 
-		void OnCompileDotnetScript (int msg_id, CancellationToken token)
+		void OnCompileDotnetScript (MessageId msg_id, CancellationToken token)
 		{
 			var o = JObject.FromObject (new { });
 
 			SendResponse (msg_id, Result.Ok (o), token);
-
 		}
 
-		async Task OnGetScriptSource (int msg_id, string script_id, CancellationToken token)
+		async Task OnGetScriptSource (MessageId msg_id, string script_id, CancellationToken token)
 		{
 			var id = new SourceId (script_id);
 			var src_file = store.GetFileById (id);
@@ -753,6 +777,16 @@ namespace WsProxy {
 			try {
 				var uri = new Uri (src_file.Url);
 				if (uri.IsFile && File.Exists(uri.LocalPath)) {
+					using (var f = new StreamReader (File.Open (uri.LocalPath, FileMode.Open))) {
+						await res.WriteAsync (await f.ReadToEndAsync ());
+					}
+
+					var o = JObject.FromObject (new {
+						scriptSource = res.ToString ()
+					});
+
+					SendResponse (msg_id, Result.Ok (o), token);
+				} else if (src_file.SourceUri.IsFile && File.Exists(src_file.SourceUri.LocalPath)) {
 					using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
 						await res.WriteAsync (await f.ReadToEndAsync ());
 					}
@@ -789,4 +823,4 @@ namespace WsProxy {
 			}
 		}
 	}
-}
+}
\ No newline at end of file