Browse Source

Update mono aspnetcore scenarios to .NET 5.0 (#6037)

Ben Adams 4 years ago
parent
commit
9e494d2ed8
69 changed files with 1918 additions and 1341 deletions
  1. 2 0
      frameworks/CSharp/aspnetcore-mono/.gitignore
  2. 57 14
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.csproj
  3. 1 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/Scenarios.cs
  4. 7 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/FortunesController.cs
  5. 15 5
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs
  6. 15 5
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs
  7. 15 4
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs
  8. 24 37
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/BatchUpdateString.cs
  9. 113 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/DapperDb.cs
  10. 23 25
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs
  11. 19 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IDb.cs
  12. 10 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IRandom.cs
  13. 1 1
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Random.cs
  14. 35 35
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/RawDb.cs
  15. 0 9
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.cs
  16. 9 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Linker.xml
  17. 51 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesDapperMiddleware.cs
  18. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesDapperMiddleware.cs
  19. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs
  20. 60 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryDapperMiddleware.cs
  21. 31 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151113004227_Initial.Designer.cs
  22. 32 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151113004227_Initial.cs
  23. 42 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151124205054_Fortune.Designer.cs
  24. 32 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151124205054_Fortune.cs
  25. 41 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/ApplicationDbContextModelSnapshot.cs
  26. 8 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/NuGet.Config
  27. 7 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Program.cs
  28. 52 26
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs
  29. 1 1
      frameworks/CSharp/aspnetcore-mono/Benchmarks/appsettings.mysql.json
  30. 11 2
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/AsciiString.cs
  31. 16 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Caching.cs
  32. 13 20
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
  33. 198 40
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
  34. 15 24
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Json.cs
  35. 12 18
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs
  36. 8 27
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.PlainText.cs
  37. 12 18
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs
  38. 12 18
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Updates.cs
  39. 110 88
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs
  40. 18 34
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs
  41. 63 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BufferExtensions.cs
  42. 52 4
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BufferWriter.cs
  43. 27 40
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs
  44. 17 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/CachedWorld.cs
  45. 12 12
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Fortune.cs
  46. 172 78
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs
  47. 13 8
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/DateHeader.cs
  48. 0 1
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/IHttpConnection.cs
  49. 9 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Linker.xml
  50. 8 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/NuGet.Config
  51. 57 12
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/PlatformBenchmarks.csproj
  52. 30 10
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs
  53. 1 1
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/StringBuilderCache.cs
  54. 0 106
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsNumeric.cs
  55. 0 332
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsText.cs
  56. 3 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/appsettings.json
  57. 4 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/appsettings.postgresql.json
  58. 0 12
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-ef-pg.dockerfile
  59. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-my.dockerfile
  60. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-pg.dockerfile
  61. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc.dockerfile
  62. 0 12
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-ef-pg.dockerfile
  63. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-my.dockerfile
  64. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-pg.dockerfile
  65. 3 3
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw.dockerfile
  66. 4 5
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-my.dockerfile
  67. 5 6
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-pg.dockerfile
  68. 4 4
      frameworks/CSharp/aspnetcore-mono/aspcore-mono.dockerfile
  69. 167 229
      frameworks/CSharp/aspnetcore-mono/benchmark_config.json

+ 2 - 0
frameworks/CSharp/aspnetcore-mono/.gitignore

@@ -36,3 +36,5 @@ BenchmarkDotNet.Artifacts/
 BDN.Generated/
 BDN.Generated/
 binaries/
 binaries/
 global.json
 global.json
+mono-rp/
+mono-rp.zip

+ 57 - 14
frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.csproj

@@ -1,27 +1,70 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net471</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
-    <RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
+    <EnableTargetingPackDownload>false</EnableTargetingPackDownload>
+    <PublishTrimmed>true</PublishTrimmed>
+    <_TrimmerDefaultAction>link</_TrimmerDefaultAction>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
     <None Update="wwwroot/**" CopyToOutputDirectory="PreserveNewest" />
     <None Update="wwwroot/**" CopyToOutputDirectory="PreserveNewest" />
     <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
     <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
-    <None Include="appsettings.postgresql.json" CopyToOutputDirectory="PreserveNewest" />
-    <None Include="appsettings.mysql.json" CopyToOutputDirectory="PreserveNewest" />
+    <None Include="appsettings.postgresql.json" />
+    <None Include="appsettings.mysql.json" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0"/>
-    <PackageReference Include="Microsoft.AspNetCore" Version="2.1.7" />
-    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.3" />
-    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
-    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
-    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.1.3" />
-    <PackageReference Include="MySqlConnector" Version="0.62.0" />
-    <PackageReference Include="Npgsql" Version="4.1.3.1" />
-    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
-    <PackageReference Include="System.Text.Json" Version="4.7.1" />
+    <TrimmerRootDescriptor Include="Linker.xml" />
   </ItemGroup>
   </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0-rc.1.20451.17" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-rc.1.20451.13" />
+
+    <PackageReference Include="Dapper" Version="2.0.35" />
+    <PackageReference Include="MySqlConnector" Version="1.0.1" />
+    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.0-rc1" />
+    <PackageReference Include="System.IO.Pipelines" Version="5.0.0-rc.1.20451.14" />
+  </ItemGroup>
+
+  <!-- Redirect "dotnet publish" to "mono-rp" folder-->
+  <Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
+    <PropertyGroup>
+      <MonoRuntimeVer>5.0.0-rc.2.20474.8</MonoRuntimeVer>
+    </PropertyGroup>
+    <DownloadFile SourceUrl="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/flat2/microsoft.netcore.app.runtime.mono.llvm.aot.$(RuntimeIdentifier)/$(MonoRuntimeVer)/microsoft.netcore.app.runtime.mono.llvm.aot.$(RuntimeIdentifier).$(MonoRuntimeVer).nupkg" DestinationFolder="$(MSBuildThisFileDirectory)" DestinationFileName="mono-rp.zip">
+    </DownloadFile>
+    <Unzip SourceFiles="mono-rp.zip" DestinationFolder="mono-rp" />
+    <ItemGroup>
+      <RuntimePack>
+        <PackageDirectory>mono-rp</PackageDirectory>
+      </RuntimePack>
+    </ItemGroup>
+    <Message Text="New PackageDirectory: %(RuntimePack.PackageDirectory)" Importance="high" />
+  </Target>
+
+  <Target Name="AspNetCore" AfterTargets="ComputeResolvedFilesToPublishList">
+    <ItemGroup>
+      <AspNetCoreFiles Include="$(MSBuildBinPath)/../../shared/Microsoft.AspNetCore.App/**/*.dll" />
+    </ItemGroup>
+    <ItemGroup>
+      <ExtraFiles Include="%(RuntimePack.PackageDirectory)/runtimes/$(RuntimeIdentifier)/lib/$(TargetFramework)/*.dll" />
+    </ItemGroup>
+
+    <Copy SourceFiles="@(AspNetCoreFiles)" DestinationFolder="$(PublishDir)" />
+    <Copy SourceFiles="@(ExtraFiles)" DestinationFolder="$(PublishDir)" />
+  </Target>
+  
+  <!-- TODO: remove once https://github.com/dotnet/runtime/pull/42729 is merged and nugets are updated -->
+  <Target Name="hack" AfterTargets="Publish">
+    <ItemGroup>
+      <HostingFiles Include="$(MSBuildBinPath)/../../host/fxr/**/libhostfxr.*" />
+      <PolicyFiles Include="$(MSBuildBinPath)/../../shared/Microsoft.NETCore.App/**/libhostpolicy.*" />
+    </ItemGroup>
+
+    <Copy SourceFiles="@(HostingFiles)" DestinationFolder="$(PublishDir)" />
+    <Copy SourceFiles="@(PolicyFiles)" DestinationFolder="$(PublishDir)" />
+  </Target>
 </Project>
 </Project>

+ 1 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/Scenarios.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Linq.Expressions;
 using System.Reflection;
 using System.Reflection;
+using Microsoft.EntityFrameworkCore.Infrastructure;
 using Microsoft.EntityFrameworkCore.Internal;
 using Microsoft.EntityFrameworkCore.Internal;
 
 
 namespace Benchmarks.Configuration
 namespace Benchmarks.Configuration

+ 7 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/FortunesController.cs

@@ -18,6 +18,13 @@ namespace Benchmarks.Controllers
             return View("Fortunes", await db.LoadFortunesRows());
             return View("Fortunes", await db.LoadFortunesRows());
         }
         }
 
 
+        [HttpGet("dapper")]
+        public async Task<IActionResult> Dapper()
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<DapperDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
+        }
+
         [HttpGet("ef")]
         [HttpGet("ef")]
         public async Task<IActionResult> Ef()
         public async Task<IActionResult> Ef()
         {
         {

+ 15 - 5
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs

@@ -13,19 +13,29 @@ namespace Benchmarks.Controllers
     {
     {
         [HttpGet("raw")]
         [HttpGet("raw")]
         [Produces("application/json")]
         [Produces("application/json")]
-        public Task<WorldRaw[]> Raw(int queries = 1)
+        public Task<World[]> Raw(int queries = 1)
         {
         {
-            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
-            return db.LoadMultipleQueriesRows(queries);
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
         }
         }
 
 
         [HttpGet("ef")]
         [HttpGet("ef")]
         [Produces("application/json")]
         [Produces("application/json")]
         public Task<World[]> Ef(int queries = 1)
         public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
         {
         {
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
             return db.LoadMultipleQueriesRows(queries);
             return db.LoadMultipleQueriesRows(queries);
         }
         }
     }
     }

+ 15 - 5
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs

@@ -13,19 +13,29 @@ namespace Benchmarks.Controllers
     {
     {
         [HttpGet("raw")]
         [HttpGet("raw")]
         [Produces("application/json")]
         [Produces("application/json")]
-        public Task<WorldRaw[]> Raw(int queries = 1)
+        public Task<World[]> Raw(int queries = 1)
         {
         {
-            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
-            return db.LoadMultipleUpdatesRows(queries);
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
         }
         }
 
 
         [HttpGet("ef")]
         [HttpGet("ef")]
         [Produces("application/json")]
         [Produces("application/json")]
         public Task<World[]> Ef(int queries = 1)
         public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
         {
         {
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
             queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
-            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
             return db.LoadMultipleUpdatesRows(queries);
             return db.LoadMultipleUpdatesRows(queries);
         }
         }
     }
     }

+ 15 - 4
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs

@@ -13,17 +13,28 @@ namespace Benchmarks.Controllers
     {
     {
         [HttpGet("raw")]
         [HttpGet("raw")]
         [Produces("application/json")]
         [Produces("application/json")]
-        public Task<WorldRaw> Raw()
+        public Task<World> Raw()
         {
         {
-            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
-            return db.LoadSingleQueryRow();
+            return ExecuteQuery<RawDb>();
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World> Dapper()
+        {
+            return ExecuteQuery<DapperDb>();
         }
         }
 
 
         [HttpGet("ef")]
         [HttpGet("ef")]
         [Produces("application/json")]
         [Produces("application/json")]
         public Task<World> Ef()
         public Task<World> Ef()
         {
         {
-            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            return ExecuteQuery<EfDb>();
+        }
+
+        private Task<World> ExecuteQuery<T>() where T : IDb
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
             return db.LoadSingleQueryRow();
             return db.LoadSingleQueryRow();
         }
         }
     }
     }

+ 24 - 37
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/BatchUpdateString.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // 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. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 
+using Benchmarks.Configuration;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
@@ -10,48 +11,34 @@ namespace Benchmarks.Data
     internal class BatchUpdateString
     internal class BatchUpdateString
     {
     {
         private const int MaxBatch = 500;
         private const int MaxBatch = 500;
-        private static string[] _queries = new string[MaxBatch];
-        
-        public static IList<BatchUpdateString> Strings { get; } =
-            Enumerable.Range(0, MaxBatch)
-                      .Select(i => new BatchUpdateString
-                      {
-                          Id = $"Id_{i}",
-                          Random = $"Random_{i}",
-                          BatchSize = i
-                      }).ToArray();
-
-        private int BatchSize { get; set; }
-        public string Id { get; set; }
-        public string Random { get; set; }
-        public string UpdateQuery => _queries[BatchSize] ?? CreateQuery(BatchSize);
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private string CreateQuery(int batchSize)
+
+        private static string[] _queries = new string[MaxBatch+1];
+
+        public static DatabaseServer DatabaseServer;
+
+        public static string Query(int batchSize)
         {
         {
-            var sb = StringBuilderCache.Acquire();
-            foreach (var q in Enumerable.Range(0, batchSize + 1)
-                .Select(i => $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"))
+            if (_queries[batchSize] != null)
             {
             {
-                sb.Append(q);
+                return _queries[batchSize];
             }
             }
-            var query = sb.ToString();
-            _queries[batchSize] = query;
-            return query;
-        }
 
 
-        public static void Initalize()
-        {
-            Observe(Strings[0].UpdateQuery);
-            Observe(Strings[4].UpdateQuery);
-            Observe(Strings[9].UpdateQuery);
-            Observe(Strings[14].UpdateQuery);
-            Observe(Strings[19].UpdateQuery);
-        }
+            var lastIndex = batchSize - 1;
 
 
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static void Observe(string query)
-        {
+            var sb = StringBuilderCache.Acquire();
+
+            if (DatabaseServer == DatabaseServer.PostgreSql)
+            {
+                sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
+                Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@Id_{i}, @Random_{i}), "));
+                sb.Append($"(@Id_{lastIndex}, @Random_{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
+            }
+            else
+            {
+                Enumerable.Range(0, batchSize).ToList().ForEach(i => sb.Append($"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"));
+            }
+
+            return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
         }
         }
     }
     }
 }
 }

+ 113 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/DapperDb.cs

@@ -0,0 +1,113 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Dynamic;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Dapper;
+using Microsoft.Extensions.Options;
+
+namespace Benchmarks.Data
+{
+    public class DapperDb : IDb
+    {
+        private static readonly Comparison<World> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
+
+        private readonly IRandom _random;
+        private readonly DbProviderFactory _dbProviderFactory;
+        private readonly string _connectionString;
+
+        public DapperDb(IRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
+        {
+            _random = random;
+            _dbProviderFactory = dbProviderFactory;
+            _connectionString = appSettings.Value.ConnectionString;
+        }
+
+        public async Task<World> LoadSingleQueryRow()
+        {
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+
+                // Note: Don't need to open connection if only doing one thing; let dapper do it
+                return await ReadSingleRow(db);
+            }
+        }
+
+        Task<World> ReadSingleRow(DbConnection db)
+        {
+            return db.QueryFirstOrDefaultAsync<World>(
+                    "SELECT id, randomnumber FROM world WHERE id = @Id",
+                    new { Id = _random.Next(1, 10001) });
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var results = new World[count];
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                for (int i = 0; i < count; i++)
+                {
+                    results[i] = await ReadSingleRow(db);
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            IDictionary<string, object> parameters = new ExpandoObject();
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                var results = new World[count];
+                for (int i = 0; i < count; i++)
+                {
+                    results[i] = await ReadSingleRow(db);
+                }
+
+                for (int i = 0; i < count; i++)
+                {
+                    var randomNumber = _random.Next(1, 10001);
+                    parameters[$"@Random_{i}"] = randomNumber;
+                    parameters[$"@Id_{i}"] = results[i].Id;
+
+                    results[i].RandomNumber = randomNumber;
+                }
+
+                await db.ExecuteAsync(BatchUpdateString.Query(count), parameters);
+                return results;
+            }
+
+        }
+
+        public async Task<List<Fortune>> LoadFortunesRows()
+        {
+            List<Fortune> result;
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+
+                // Note: don't need to open connection if only doing one thing; let dapper do it
+                result = (await db.QueryAsync<Fortune>("SELECT id, message FROM fortune")).AsList();
+            }
+
+            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Sort();
+
+            return result;
+        }
+    }
+}

+ 23 - 25
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs

@@ -1,28 +1,25 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // 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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
+using Benchmarks.Configuration;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Benchmarks.Configuration;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Query;
-using Microsoft.Extensions.Options;
 
 
 namespace Benchmarks.Data
 namespace Benchmarks.Data
 {
 {
-    public class EfDb
+    public class EfDb : IDb
     {
     {
-        private readonly ConcurrentRandom _random;
+        private readonly IRandom _random;
         private readonly ApplicationDbContext _dbContext;
         private readonly ApplicationDbContext _dbContext;
-        private readonly bool _useBatchUpdate;
 
 
-        public EfDb(ConcurrentRandom random, ApplicationDbContext dbContext, IOptions<AppSettings> appSettings)
+        public EfDb(IRandom random, ApplicationDbContext dbContext, IOptions<AppSettings> appSettings)
         {
         {
             _random = random;
             _random = random;
             _dbContext = dbContext;
             _dbContext = dbContext;
-            _useBatchUpdate = appSettings.Value.Database != DatabaseServer.PostgreSql;
         }
         }
 
 
         private static readonly Func<ApplicationDbContext, int, Task<World>> _firstWorldQuery
         private static readonly Func<ApplicationDbContext, int, Task<World>> _firstWorldQuery
@@ -57,29 +54,25 @@ namespace Benchmarks.Data
         public async Task<World[]> LoadMultipleUpdatesRows(int count)
         public async Task<World[]> LoadMultipleUpdatesRows(int count)
         {
         {
             var results = new World[count];
             var results = new World[count];
-            var random = new Random();
-            int i = 0;
+            int currentValue, newValue;
 
 
-            var ids = Enumerable.Range(1, 10000).OrderBy(x => random.Next()).Take(count);
+            var ids = Enumerable.Range(1, 10000).Select(x => _random.Next(1, 10001)).Distinct().Take(count).ToArray();
 
 
-            foreach (int id in ids)
+            for (var i = 0; i < count; i++)
             {
             {
-                // TODO: compiled queries are not supported in EF 3.0-preview7
-                // var result = await _firstWorldTrackedQuery(_dbContext, id);
-
-                var result = await _dbContext.World.AsTracking().FirstAsync(w => w.Id == id);
+                results[i] = await _firstWorldTrackedQuery(_dbContext, ids[i]);
 
 
-                int oldId = (int)_dbContext.Entry(result).Property("RandomNumber").CurrentValue;
-                int newId;
+                currentValue = results[i].RandomNumber;
 
 
                 do
                 do
                 {
                 {
-                    newId = _random.Next(1, 10001);
-                } while (oldId == newId);
+                    newValue = _random.Next(1, 10001);
+                }
+                while (newValue == currentValue);
 
 
-                _dbContext.Entry(result).Property("RandomNumber").CurrentValue = newId;
+                results[i].RandomNumber = newValue;
 
 
-                results[i++] = result;
+                _dbContext.Entry(results[i]).State = EntityState.Modified;
             }
             }
 
 
             await _dbContext.SaveChangesAsync();
             await _dbContext.SaveChangesAsync();
@@ -87,12 +80,17 @@ namespace Benchmarks.Data
             return results;
             return results;
         }
         }
 
 
-        private static readonly Func<ApplicationDbContext, AsyncEnumerable<Fortune>> _fortunesQuery
+        private static readonly Func<ApplicationDbContext, IAsyncEnumerable<Fortune>> _fortunesQuery
             = EF.CompileAsyncQuery((ApplicationDbContext context) => context.Fortune);
             = EF.CompileAsyncQuery((ApplicationDbContext context) => context.Fortune);
 
 
         public async Task<List<Fortune>> LoadFortunesRows()
         public async Task<List<Fortune>> LoadFortunesRows()
         {
         {
-            var result = await _fortunesQuery(_dbContext).ToListAsync();
+            var result = new List<Fortune>();
+
+            await foreach (var element in _fortunesQuery(_dbContext))
+            {
+                result.Add(element);
+            }
 
 
             result.Add(new Fortune { Message = "Additional fortune added at request time." });
             result.Add(new Fortune { Message = "Additional fortune added at request time." });
             result.Sort();
             result.Sort();

+ 19 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IDb.cs

@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Benchmarks.Data
+{
+    public interface IDb
+    {
+        Task<World> LoadSingleQueryRow();
+
+        Task<World[]> LoadMultipleQueriesRows(int count);
+
+        Task<World[]> LoadMultipleUpdatesRows(int count);
+
+        Task<List<Fortune>> LoadFortunesRows();
+    }
+}

+ 10 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IRandom.cs

@@ -0,0 +1,10 @@
+// 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 Benchmarks.Data
+{
+    public interface IRandom
+    {
+        int Next(int minValue, int maxValue);
+    }
+}

+ 1 - 1
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Random.cs

@@ -7,7 +7,7 @@ using System.Threading;
 
 
 namespace Benchmarks.Data
 namespace Benchmarks.Data
 {
 {
-    public class ConcurrentRandom
+    public class DefaultRandom : IRandom
     {
     {
         private static int nextSeed = 0;
         private static int nextSeed = 0;
 
 

+ 35 - 35
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/RawDb.cs

@@ -11,22 +11,22 @@ using Microsoft.Extensions.Options;
 
 
 namespace Benchmarks.Data
 namespace Benchmarks.Data
 {
 {
-    public class RawDb
+    public class RawDb : IDb
     {
     {
-        private static readonly Comparison<WorldRaw> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
+        private static readonly Comparison<World> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
 
 
-        private readonly ConcurrentRandom _random;
+        private readonly IRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
         private readonly string _connectionString;
 
 
-        public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
+        public RawDb(IRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
         {
         {
             _random = random;
             _random = random;
             _dbProviderFactory = dbProviderFactory;
             _dbProviderFactory = dbProviderFactory;
             _connectionString = appSettings.Value.ConnectionString;
             _connectionString = appSettings.Value.ConnectionString;
         }
         }
 
 
-        public async Task<WorldRaw> LoadSingleQueryRow()
+        public async Task<World> LoadSingleQueryRow()
         {
         {
             using (var db = _dbProviderFactory.CreateConnection())
             using (var db = _dbProviderFactory.CreateConnection())
             {
             {
@@ -40,13 +40,13 @@ namespace Benchmarks.Data
             }
             }
         }
         }
 
 
-        async Task<WorldRaw> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
         {
         {
             using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
             using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
             {
             {
                 await rdr.ReadAsync();
                 await rdr.ReadAsync();
 
 
-                return new WorldRaw
+                return new World
                 {
                 {
                     Id = rdr.GetInt32(0),
                     Id = rdr.GetInt32(0),
                     RandomNumber = rdr.GetInt32(1)
                     RandomNumber = rdr.GetInt32(1)
@@ -64,60 +64,60 @@ namespace Benchmarks.Data
             id.Value = _random.Next(1, 10001);
             id.Value = _random.Next(1, 10001);
             cmd.Parameters.Add(id);
             cmd.Parameters.Add(id);
 
 
+            (cmd as MySqlConnector.MySqlCommand)?.Prepare();
+
             return cmd;
             return cmd;
         }
         }
 
 
-        public async Task<WorldRaw[]> LoadMultipleQueriesRows(int count)
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
         {
+            var result = new World[count];
+
             using (var db = _dbProviderFactory.CreateConnection())
             using (var db = _dbProviderFactory.CreateConnection())
             {
             {
                 db.ConnectionString = _connectionString;
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
-                return await LoadMultipleRows(count, db);
-            }
-        }
-
-        private async Task<WorldRaw[]> LoadMultipleRows(int count, DbConnection db)
-        {
-            using (var cmd = CreateReadCommand(db))
-            {
-                cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
-
-                var result = new WorldRaw[count];
-                for (int i = 0; i < result.Length; i++)
+                using (var cmd = CreateReadCommand(db))
                 {
                 {
-                    result[i] = await ReadSingleRow(db, cmd);
-                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    for (int i = 0; i < count; i++)
+                    {
+                        result[i] = await ReadSingleRow(db, cmd);
+                        cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
                 }
                 }
-                return result;
             }
             }
+
+            return result;
         }
         }
 
 
-        public async Task<WorldRaw[]> LoadMultipleUpdatesRows(int count)
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
         {
         {
             using (var db = _dbProviderFactory.CreateConnection())
             using (var db = _dbProviderFactory.CreateConnection())
             {
             {
                 db.ConnectionString = _connectionString;
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
 
 
-                var results = await LoadMultipleRows(count, db);
-
-                // Postgres has problems with deadlocks when these aren't sorted
-                Array.Sort<WorldRaw>(results, WorldSortComparison);
-
                 using (var updateCmd = db.CreateCommand())
                 using (var updateCmd = db.CreateCommand())
+                using (var queryCmd = CreateReadCommand(db))
                 {
                 {
-                    for (int i = 0; i < results.Length; i++)
+                    var results = new World[count];
+                    for (int i = 0; i < count; i++)
                     {
                     {
-                        var strings = BatchUpdateString.Strings[i];
+                        results[i] = await ReadSingleRow(db, queryCmd);
+                        queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
+
+                    updateCmd.CommandText = BatchUpdateString.Query(count);
 
 
+                    for (int i = 0; i < count; i++)
+                    {
                         var id = updateCmd.CreateParameter();
                         var id = updateCmd.CreateParameter();
-                        id.ParameterName = strings.Id;
+                        id.ParameterName = $"@Id_{i}";
                         id.DbType = DbType.Int32;
                         id.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(id);
                         updateCmd.Parameters.Add(id);
 
 
                         var random = updateCmd.CreateParameter();
                         var random = updateCmd.CreateParameter();
-                        random.ParameterName = strings.Random;
+                        random.ParameterName = $"@Random_{i}";
                         random.DbType = DbType.Int32;
                         random.DbType = DbType.Int32;
                         updateCmd.Parameters.Add(random);
                         updateCmd.Parameters.Add(random);
 
 
@@ -127,9 +127,7 @@ namespace Benchmarks.Data
                         results[i].RandomNumber = randomNumber;
                         results[i].RandomNumber = randomNumber;
                     }
                     }
 
 
-                    updateCmd.CommandText = BatchUpdateString.Strings[count - 1].UpdateQuery;
                     await updateCmd.ExecuteNonQueryAsync();
                     await updateCmd.ExecuteNonQueryAsync();
-
                     return results;
                     return results;
                 }
                 }
             }
             }
@@ -147,6 +145,8 @@ namespace Benchmarks.Data
                 db.ConnectionString = _connectionString;
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
 
 
+                (cmd as MySqlConnector.MySqlCommand)?.Prepare();
+
                 using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                 using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                 {
                 {
                     while (await rdr.ReadAsync())
                     while (await rdr.ReadAsync())

+ 0 - 9
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.cs

@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.ComponentModel.DataAnnotations.Schema;
 using System.ComponentModel.DataAnnotations.Schema;
-using System.Runtime.InteropServices;
 
 
 namespace Benchmarks.Data
 namespace Benchmarks.Data
 {
 {
@@ -15,12 +14,4 @@ namespace Benchmarks.Data
         [Column("randomnumber")]
         [Column("randomnumber")]
         public int RandomNumber { get; set; }
         public int RandomNumber { get; set; }
     }
     }
-
-    [StructLayout(LayoutKind.Sequential, Size = 8)]
-    public struct WorldRaw
-    {
-        public int Id { get; set; }
-
-        public int RandomNumber { get; set; }
-    }
 }
 }

+ 9 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Linker.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- See: https://github.com/mono/linker/blob/master/docs/data-formats.md -->
+<linker>
+	<assembly fullname="Microsoft.AspNetCore.Server.Kestrel.Core">
+		<type fullname="Microsoft.AspNetCore.Server.Kestrel.Core.Internal.CertificateConfig" />
+	</assembly>
+  <assembly fullname="Microsoft.AspNetCore.Hosting.Abstractions" />
+  <assembly fullname="System.Threading.Channels" />
+</linker>

+ 51 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesDapperMiddleware.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 System;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Middleware
+{
+    public class FortunesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesDapper));
+
+        private readonly RequestDelegate _next;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesDapperMiddleware(RequestDelegate next, HtmlEncoder htmlEncoder)
+        {
+            _next = next;
+            _htmlEncoder = htmlEncoder;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<DapperDb>();
+                var rows = await db.LoadFortunesRows();
+
+                await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class FortunesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseFortunesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<FortunesDapperMiddleware>();
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesDapperMiddleware.cs

@@ -0,0 +1,62 @@
+// 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.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleQueriesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiQueryDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleQueriesDapperMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+
+                var db = httpContext.RequestServices.GetService<DapperDb>();
+                var rows = await db.LoadMultipleQueriesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleQueriesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleQueriesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleQueriesDapperMiddleware>();
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs

@@ -0,0 +1,62 @@
+// 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.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleUpdatesDapperMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+
+                var db = httpContext.RequestServices.GetService<DapperDb>();
+                var rows = await db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesDapperMiddleware>();
+        }
+    }
+}

+ 60 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryDapperMiddleware.cs

@@ -0,0 +1,60 @@
+// 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.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class SingleQueryDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbSingleQueryDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public SingleQueryDapperMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<DapperDb>();
+                var row = await db.LoadSingleQueryRow();
+
+                var result = JsonConvert.SerializeObject(row, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class SingleQueryDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseSingleQueryDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<SingleQueryDapperMiddleware>();
+        }
+    }
+}

+ 31 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151113004227_Initial.Designer.cs

@@ -0,0 +1,31 @@
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Benchmarks.Data;
+
+namespace Benchmarks.Migrations
+{
+    [DbContext(typeof(ApplicationDbContext))]
+    [Migration("20151113004227_Initial")]
+    partial class Initial
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+            modelBuilder
+                .HasAnnotation("ProductVersion", "7.0.0-rc2-16329")
+                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+            modelBuilder.Entity("AspNet5.Data.World", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd();
+
+                    b.Property<int>("RandomNumber");
+
+                    b.HasKey("Id");
+                });
+        }
+    }
+}

+ 32 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151113004227_Initial.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Metadata;
+using System.Text;
+
+namespace Benchmarks.Migrations
+{
+    public partial class Initial : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "World",
+                columns: table => new
+                {
+                    Id = table.Column<int>(nullable: false)
+                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                    RandomNumber = table.Column<int>(nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_World", x => x.Id);
+                });
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable("World");
+        }
+    }
+}

+ 42 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151124205054_Fortune.Designer.cs

@@ -0,0 +1,42 @@
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Benchmarks.Data;
+
+namespace Benchmarks.Migrations
+{
+    [DbContext(typeof(ApplicationDbContext))]
+    [Migration("20151124205054_Fortune")]
+    partial class Fortune
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+            modelBuilder
+                .HasAnnotation("ProductVersion", "7.0.0-rc2-16413")
+                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+            modelBuilder.Entity("Benchmarks.Data.Fortune", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd();
+
+                    b.Property<string>("Message")
+                        .HasAnnotation("MaxLength", 2048);
+
+                    b.HasKey("Id");
+                });
+
+            modelBuilder.Entity("Benchmarks.Data.World", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd();
+
+                    b.Property<int>("RandomNumber");
+
+                    b.HasKey("Id");
+                });
+        }
+    }
+}

+ 32 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/20151124205054_Fortune.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Metadata;
+
+namespace Benchmarks.Migrations
+{
+    public partial class Fortune : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "Fortune",
+                columns: table => new
+                {
+                    Id = table.Column<int>(nullable: false)
+                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                    Message = table.Column<string>(nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_Fortune", x => x.Id);
+                });
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "Fortune");
+        }
+    }
+}

+ 41 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Migrations/ApplicationDbContextModelSnapshot.cs

@@ -0,0 +1,41 @@
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Benchmarks.Data;
+
+namespace Benchmarks.Migrations
+{
+    [DbContext(typeof(ApplicationDbContext))]
+    partial class ApplicationDbContextModelSnapshot : ModelSnapshot
+    {
+        protected override void BuildModel(ModelBuilder modelBuilder)
+        {
+            modelBuilder
+                .HasAnnotation("ProductVersion", "7.0.0-rc2-16413")
+                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+            modelBuilder.Entity("Benchmarks.Data.Fortune", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd();
+
+                    b.Property<string>("Message")
+                        .HasAnnotation("MaxLength", 2048);
+
+                    b.HasKey("Id");
+                });
+
+            modelBuilder.Entity("Benchmarks.Data.World", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd();
+
+                    b.Property<int>("RandomNumber");
+
+                    b.HasKey("Id");
+                });
+        }
+    }
+}

+ 8 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/NuGet.Config

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <packageSources>
+    <clear />
+    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
+    <add key="dotnet5" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json" />
+  </packageSources>
+</configuration>

+ 7 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Program.cs

@@ -10,6 +10,8 @@ using Benchmarks.Configuration;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
 
 
 namespace Benchmarks
 namespace Benchmarks
 {
 {
@@ -24,6 +26,11 @@ namespace Benchmarks
             Console.WriteLine();
             Console.WriteLine();
             Console.WriteLine("ASP.NET Core Benchmarks");
             Console.WriteLine("ASP.NET Core Benchmarks");
             Console.WriteLine("-----------------------");
             Console.WriteLine("-----------------------");
+            bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null;
+            Console.WriteLine("Runtime " + (isMono ? "Mono" : "CoreCLR"));
+            Console.WriteLine(typeof(object).Assembly.FullName);
+            Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly());
+            Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
 
 
             Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
             Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
             Console.WriteLine($"WebHostBuilder loading from: {typeof(WebHostBuilder).GetTypeInfo().Assembly.Location}");
             Console.WriteLine($"WebHostBuilder loading from: {typeof(WebHostBuilder).GetTypeInfo().Assembly.Location}");

+ 52 - 26
frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs

@@ -3,6 +3,7 @@
 
 
 using System;
 using System;
 using System.Data.Common;
 using System.Data.Common;
+using System.Data.SqlClient;
 using System.Text.Encodings.Web;
 using System.Text.Encodings.Web;
 using System.Text.Unicode;
 using System.Text.Unicode;
 using Benchmarks.Configuration;
 using Benchmarks.Configuration;
@@ -10,17 +11,18 @@ using Benchmarks.Data;
 using Benchmarks.Middleware;
 using Benchmarks.Middleware;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
-using MySql.Data.MySqlClient;
+using MySqlConnector;
 using Npgsql;
 using Npgsql;
 
 
 namespace Benchmarks
 namespace Benchmarks
 {
 {
     public class Startup
     public class Startup
     {
     {
-        public Startup(IHostingEnvironment hostingEnv, Scenarios scenarios)
+        public Startup(IWebHostEnvironment hostingEnv, Scenarios scenarios)
         {
         {
             // Set up configuration sources.
             // Set up configuration sources.
             var builder = new ConfigurationBuilder()
             var builder = new ConfigurationBuilder()
@@ -49,9 +51,12 @@ namespace Benchmarks
             services.AddSingleton(Scenarios);
             services.AddSingleton(Scenarios);
 
 
             // Common DB services
             // Common DB services
-            services.AddSingleton<ConcurrentRandom>();
+            services.AddSingleton<IRandom, DefaultRandom>();
+            services.AddEntityFrameworkSqlServer();
 
 
             var appSettings = Configuration.Get<AppSettings>();
             var appSettings = Configuration.Get<AppSettings>();
+            BatchUpdateString.DatabaseServer = appSettings.Database;
+
             Console.WriteLine($"Database: {appSettings.Database}");
             Console.WriteLine($"Database: {appSettings.Database}");
 
 
             if (appSettings.Database == DatabaseServer.PostgreSql)
             if (appSettings.Database == DatabaseServer.PostgreSql)
@@ -70,7 +75,7 @@ namespace Benchmarks
             {
             {
                 if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
                 if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
                 {
                 {
-                    services.AddSingleton<DbProviderFactory>(MySqlClientFactory.Instance);
+                    services.AddSingleton<DbProviderFactory>(MySqlConnectorFactory.Instance);
                 }
                 }
             }
             }
 
 
@@ -84,6 +89,11 @@ namespace Benchmarks
                 services.AddScoped<RawDb>();
                 services.AddScoped<RawDb>();
             }
             }
 
 
+            if (Scenarios.Any("Dapper"))
+            {
+                services.AddScoped<DapperDb>();
+            }
+
             if (Scenarios.Any("Fortunes"))
             if (Scenarios.Any("Fortunes"))
             {
             {
                 var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
                 var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
@@ -98,12 +108,8 @@ namespace Benchmarks
             {
             {
                 var mvcBuilder = services
                 var mvcBuilder = services
                     .AddMvcCore()
                     .AddMvcCore()
-                    .AddControllersAsServices();
-
-                if (Scenarios.MvcJson || Scenarios.Any("MvcDbSingle") || Scenarios.Any("MvcDbMulti"))
-                {
-                    mvcBuilder.AddJsonFormatters();
-                }
+                    .SetCompatibilityVersion(CompatibilityVersion.Latest)
+                    ;
 
 
                 if (Scenarios.MvcViews || Scenarios.Any("MvcDbFortunes"))
                 if (Scenarios.MvcViews || Scenarios.Any("MvcDbFortunes"))
                 {
                 {
@@ -126,12 +132,33 @@ namespace Benchmarks
                 app.UseJson();
                 app.UseJson();
             }
             }
 
 
+            // Fortunes endpoints
+            if (Scenarios.DbFortunesRaw)
+            {
+                app.UseFortunesRaw();
+            }
+
+            if (Scenarios.DbFortunesDapper)
+            {
+                app.UseFortunesDapper();
+            }
+
+            if (Scenarios.DbFortunesEf)
+            {
+                app.UseFortunesEf();
+            }
+
             // Single query endpoints
             // Single query endpoints
             if (Scenarios.DbSingleQueryRaw)
             if (Scenarios.DbSingleQueryRaw)
             {
             {
                 app.UseSingleQueryRaw();
                 app.UseSingleQueryRaw();
             }
             }
 
 
+            if (Scenarios.DbSingleQueryDapper)
+            {
+                app.UseSingleQueryDapper();
+            }
+
             if (Scenarios.DbSingleQueryEf)
             if (Scenarios.DbSingleQueryEf)
             {
             {
                 app.UseSingleQueryEf();
                 app.UseSingleQueryEf();
@@ -143,6 +170,11 @@ namespace Benchmarks
                 app.UseMultipleQueriesRaw();
                 app.UseMultipleQueriesRaw();
             }
             }
 
 
+            if (Scenarios.DbMultiQueryDapper)
+            {
+                app.UseMultipleQueriesDapper();
+            }
+
             if (Scenarios.DbMultiQueryEf)
             if (Scenarios.DbMultiQueryEf)
             {
             {
                 app.UseMultipleQueriesEf();
                 app.UseMultipleQueriesEf();
@@ -154,30 +186,24 @@ namespace Benchmarks
                 app.UseMultipleUpdatesRaw();
                 app.UseMultipleUpdatesRaw();
             }
             }
 
 
-            if (Scenarios.DbMultiUpdateEf)
-            {
-                app.UseMultipleUpdatesEf();
-            }
-
-            // Fortunes endpoints
-            if (Scenarios.DbFortunesRaw)
+            if (Scenarios.DbMultiUpdateDapper)
             {
             {
-                app.UseFortunesRaw();
+                app.UseMultipleUpdatesDapper();
             }
             }
 
 
-            if (Scenarios.DbFortunesEf)
+            if (Scenarios.DbMultiUpdateEf)
             {
             {
-                app.UseFortunesEf();
+                app.UseMultipleUpdatesEf();
             }
             }
 
 
             if (Scenarios.Any("Mvc"))
             if (Scenarios.Any("Mvc"))
             {
             {
-                app.UseMvc();
-            }
-
-            if (Scenarios.Any("Update"))
-            {
-                BatchUpdateString.Initalize();
+                app.UseRouting();
+            
+                app.UseEndpoints(endpoints =>
+                {
+                    endpoints.MapControllers();
+                });
             }
             }
 
 
             if (Scenarios.StaticFiles)
             if (Scenarios.StaticFiles)

+ 1 - 1
frameworks/CSharp/aspnetcore-mono/Benchmarks/appsettings.mysql.json

@@ -1,4 +1,4 @@
 {
 {
-  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;SslMode=None;ConnectionReset=false;ConnectionIdlePingTime=900;ConnectionIdleTimeout=0",
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;SslMode=None;ConnectionReset=false;ConnectionIdlePingTime=900;ConnectionIdleTimeout=0;AutoEnlist=false;DefaultCommandTimeout=0;ConnectionTimeout=0;IgnorePrepare=false;",
   "Database": "mysql"
   "Database": "mysql"
 }
 }

+ 11 - 2
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/AsciiString.cs

@@ -3,7 +3,6 @@
 
 
 using System;
 using System;
 using System.Text;
 using System.Text;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -13,6 +12,8 @@ namespace PlatformBenchmarks
 
 
         public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s);
         public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s);
 
 
+        private AsciiString(byte[] b) => _data = b;
+
         public int Length => _data.Length;
         public int Length => _data.Length;
 
 
         public ReadOnlySpan<byte> AsSpan() => _data;
         public ReadOnlySpan<byte> AsSpan() => _data;
@@ -22,7 +23,7 @@ namespace PlatformBenchmarks
 
 
         public static implicit operator AsciiString(string str) => new AsciiString(str);
         public static implicit operator AsciiString(string str) => new AsciiString(str);
 
 
-        public override string ToString() => HttpUtilities.GetAsciiStringNonNullCharacters(_data);
+        public override string ToString() => Encoding.ASCII.GetString(_data);
         public static explicit operator string(AsciiString str) => str.ToString();
         public static explicit operator string(AsciiString str) => str.ToString();
 
 
         public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data);
         public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data);
@@ -32,6 +33,14 @@ namespace PlatformBenchmarks
         public static bool operator !=(AsciiString a, AsciiString b) => !a.Equals(b);
         public static bool operator !=(AsciiString a, AsciiString b) => !a.Equals(b);
         public override bool Equals(object other) => (other is AsciiString) && Equals((AsciiString)other);
         public override bool Equals(object other) => (other is AsciiString) && Equals((AsciiString)other);
 
 
+        public static AsciiString operator +(AsciiString a, AsciiString b)
+        {
+            var result = new byte[a.Length + b.Length];
+            a._data.CopyTo(result, 0);
+            b._data.CopyTo(result, a.Length);
+            return new AsciiString(result);
+        }
+
         public override int GetHashCode()
         public override int GetHashCode()
         {
         {
             // Copied from x64 version of string.GetLegacyNonRandomizedHashCode()
             // Copied from x64 version of string.GetLegacyNonRandomizedHashCode()

+ 16 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Caching.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.IO.Pipelines;
+using System.Threading.Tasks;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task Caching(PipeWriter pipeWriter, int count)
+        {
+            OutputMultipleQueries(pipeWriter, await Db.LoadCachedQueries(count));
+        }
+    }
+}

+ 13 - 20
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs

@@ -3,42 +3,35 @@
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO.Pipelines;
 using System.IO.Pipelines;
+using System.Text.Encodings.Web;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     public partial class BenchmarkApplication
     public partial class BenchmarkApplication
     {
     {
+        private readonly static AsciiString _fortunesPreamble =
+            _http11OK +
+            _headerServer + _crlf +
+            _headerContentTypeHtml + _crlf +
+            _headerContentLength;
+
         private async Task Fortunes(PipeWriter pipeWriter)
         private async Task Fortunes(PipeWriter pipeWriter)
         {
         {
             OutputFortunes(pipeWriter, await Db.LoadFortunesRows());
             OutputFortunes(pipeWriter, await Db.LoadFortunesRows());
         }
         }
 
 
-        private static void OutputFortunes(PipeWriter pipeWriter, List<Fortune> model)
+        private void OutputFortunes(PipeWriter pipeWriter, List<Fortune> model)
         {
         {
-            var writer = GetWriter(pipeWriter);
-
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
-
-            // Server headers
-            writer.Write(_headerServer);
+            var writer = GetWriter(pipeWriter, sizeHint: 1600); // in reality it's 1361
 
 
-            // Date header
-            writer.Write(DateHeader.HeaderBytes);
-
-            // Content-Type header
-            writer.Write(_headerContentTypeHtml);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
+            writer.Write(_fortunesPreamble);
 
 
             var lengthWriter = writer;
             var lengthWriter = writer;
             writer.Write(_contentLengthGap);
             writer.Write(_contentLengthGap);
 
 
-            // End of headers
-            writer.Write(_eoh);
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
 
 
             var bodyStart = writer.Buffered;
             var bodyStart = writer.Buffered;
             // Body
             // Body
@@ -48,7 +41,7 @@ namespace PlatformBenchmarks
                 writer.Write(_fortunesRowStart);
                 writer.Write(_fortunesRowStart);
                 writer.WriteNumeric((uint)item.Id);
                 writer.WriteNumeric((uint)item.Id);
                 writer.Write(_fortunesColumn);
                 writer.Write(_fortunesColumn);
-                writer.WriteUtf8HtmlString(item.Message);
+                writer.WriteUtf8String(HtmlEncoder.Encode(item.Message));
                 writer.Write(_fortunesRowEnd);
                 writer.Write(_fortunesRowEnd);
             }
             }
             writer.Write(_fortunesTableEnd);
             writer.Write(_fortunesTableEnd);

+ 198 - 40
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs

@@ -1,10 +1,13 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System;
 using System;
 using System.Buffers;
 using System.Buffers;
 using System.IO.Pipelines;
 using System.IO.Pipelines;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 
 
@@ -17,6 +20,8 @@ namespace PlatformBenchmarks
         public PipeReader Reader { get; set; }
         public PipeReader Reader { get; set; }
         public PipeWriter Writer { get; set; }
         public PipeWriter Writer { get; set; }
 
 
+        protected HtmlEncoder HtmlEncoder { get; } = CreateHtmlEncoder();
+
         private HttpParser<ParsingAdapter> Parser { get; } = new HttpParser<ParsingAdapter>();
         private HttpParser<ParsingAdapter> Parser { get; } = new HttpParser<ParsingAdapter>();
 
 
         public async Task ExecuteAsync()
         public async Task ExecuteAsync()
@@ -37,23 +42,128 @@ namespace PlatformBenchmarks
             }
             }
         }
         }
 
 
+        private static HtmlEncoder CreateHtmlEncoder()
+        {
+            var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+            settings.AllowCharacter('\u2014');  // allow EM DASH through
+            return HtmlEncoder.Create(settings);
+        }
+
+#if !DATABASE
         private async Task ProcessRequestsAsync()
         private async Task ProcessRequestsAsync()
         {
         {
             while (true)
             while (true)
             {
             {
-                var task = Reader.ReadAsync();
+                var readResult = await Reader.ReadAsync(default);
+                var buffer = readResult.Buffer;
+                var isCompleted = readResult.IsCompleted;
+
+                if (buffer.IsEmpty && isCompleted)
+                {
+                    return;
+                }
 
 
-                if (!task.IsCompleted)
+                if (!HandleRequests(buffer, isCompleted))
                 {
                 {
-                    // No more data in the input
-                    await OnReadCompletedAsync();
+                    return;
+                }
+
+                await Writer.FlushAsync(default);
+            }
+        }
+
+        private bool HandleRequests(in ReadOnlySequence<byte> buffer, bool isCompleted)
+        {
+            var reader = new SequenceReader<byte>(buffer);
+            var writer = GetWriter(Writer, sizeHint: 160 * 16); // 160*16 is for Plaintext, for Json 160 would be enough
+
+            while (true)
+            {
+                if (!ParseHttpRequest(ref reader, isCompleted))
+                {
+                    return false;
+                }
+
+                if (_state == State.Body)
+                {
+                    ProcessRequest(ref writer);
+
+                    _state = State.StartLine;
+
+                    if (!reader.End)
+                    {
+                        // More input data to parse
+                        continue;
+                    }
+                }
+
+                // No more input or incomplete data, Advance the Reader
+                Reader.AdvanceTo(reader.Position, buffer.End);
+                break;
+            }
+
+            writer.Commit();
+            return true;
+        }
+
+        private bool ParseHttpRequest(ref SequenceReader<byte> reader, bool isCompleted)
+        {
+            var state = _state;
+
+            if (state == State.StartLine)
+            {
+#if NET5_0
+                if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
+                {
+                    state = State.Headers;
+                }
+#else
+                var unconsumedSequence = reader.Sequence.Slice(reader.Position);
+                if (Parser.ParseRequestLine(new ParsingAdapter(this), unconsumedSequence, out var consumed, out _))
+                {
+                    state = State.Headers;
+
+                    var parsedLength = unconsumedSequence.Slice(reader.Position, consumed).Length;
+                    reader.Advance(parsedLength);
+                }
+#endif
+            }
+
+            if (state == State.Headers)
+            {
+                var success = Parser.ParseHeaders(new ParsingAdapter(this), ref reader);
+
+                if (success)
+                {
+                    state = State.Body;
+                }
+            }
+
+            if (state != State.Body && isCompleted)
+            {
+                ThrowUnexpectedEndOfData();
+            }
+
+            _state = state;
+            return true;
+        }
+#else
+        private async Task ProcessRequestsAsync()
+        {
+            while (true)
+            {
+                var readResult = await Reader.ReadAsync();
+                var buffer = readResult.Buffer;
+                var isCompleted = readResult.IsCompleted;
+
+                if (buffer.IsEmpty && isCompleted)
+                {
+                    return;
                 }
                 }
 
 
-                var result = await task;
-                var buffer = result.Buffer;
                 while (true)
                 while (true)
                 {
                 {
-                    if (!ParseHttpRequest(ref buffer, result.IsCompleted, out var examined))
+                    if (!ParseHttpRequest(ref buffer, isCompleted))
                     {
                     {
                         return;
                         return;
                     }
                     }
@@ -72,63 +182,94 @@ namespace PlatformBenchmarks
                     }
                     }
 
 
                     // No more input or incomplete data, Advance the Reader
                     // No more input or incomplete data, Advance the Reader
-                    Reader.AdvanceTo(buffer.Start, examined);
+                    Reader.AdvanceTo(buffer.Start, buffer.End);
                     break;
                     break;
                 }
                 }
+
+                await Writer.FlushAsync();
             }
             }
         }
         }
 
 
-        private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted, out SequencePosition examined)
+        private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted)
         {
         {
-            examined = buffer.End;
-
-            var consumed = buffer.Start;
+            var reader = new SequenceReader<byte>(buffer);
             var state = _state;
             var state = _state;
 
 
-            if (!buffer.IsEmpty)
+            if (state == State.StartLine)
             {
             {
-                if (state == State.StartLine)
+#if NET5_0
+                if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
                 {
                 {
-                    if (Parser.ParseRequestLine(new ParsingAdapter(this), buffer, out consumed, out examined))
-                    {
-                        state = State.Headers;
-                    }
-
-                    buffer = buffer.Slice(consumed);
+                    state = State.Headers;
                 }
                 }
-
-                if (state == State.Headers)
+#else
+                var unconsumedSequence = reader.Sequence.Slice(reader.Position);
+                if (Parser.ParseRequestLine(new ParsingAdapter(this), unconsumedSequence, out var consumed, out _))
                 {
                 {
-                    if (Parser.ParseHeaders(new ParsingAdapter(this), buffer, out consumed, out examined, out int consumedBytes))
-                    {
-                        state = State.Body;
-                    }
+                    state = State.Headers;
 
 
-                    buffer = buffer.Slice(consumed);
+                    var parsedLength = unconsumedSequence.Slice(reader.Position, consumed).Length;
+                    reader.Advance(parsedLength);
                 }
                 }
+#endif
+            }
+
+            if (state == State.Headers)
+            {
+                var success = Parser.ParseHeaders(new ParsingAdapter(this), ref reader);
 
 
-                if (state != State.Body && isCompleted)
+                if (success)
                 {
                 {
-                    ThrowUnexpectedEndOfData();
+                    state = State.Body;
                 }
                 }
             }
             }
-            else if (isCompleted)
+
+            if (state != State.Body && isCompleted)
             {
             {
-                return false;
+                ThrowUnexpectedEndOfData();
             }
             }
 
 
             _state = state;
             _state = state;
+
+            if (state == State.Body)
+            {
+                // Complete request read, consumed and examined are the same (length 0)
+                buffer = buffer.Slice(reader.Position, 0);
+            }
+            else
+            {
+                // In-complete request read, consumed is current position and examined is the remaining.
+                buffer = buffer.Slice(reader.Position);
+            }
+
             return true;
             return true;
         }
         }
+#endif
 
 
-        public void OnHeader(Span<byte> name, Span<byte> value)
+#if NET5_0
+
+        public void OnStaticIndexedHeader(int index)
+        {
+        }
+
+        public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
         {
         {
         }
         }
 
 
-        public async ValueTask OnReadCompletedAsync()
+        public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
+        {
+        }
+        public void OnHeadersComplete(bool endStream)
         {
         {
-            await Writer.FlushAsync();
         }
         }
+#else
+        public void OnHeader(Span<byte> name, Span<byte> value)
+        {
+        }
+        public void OnHeadersComplete()
+        {
+        }
+#endif
 
 
         private static void ThrowUnexpectedEndOfData()
         private static void ThrowUnexpectedEndOfData()
         {
         {
@@ -143,8 +284,8 @@ namespace PlatformBenchmarks
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static BufferWriter<WriterAdapter> GetWriter(PipeWriter pipeWriter)
-            => new BufferWriter<WriterAdapter>(new WriterAdapter(pipeWriter));
+        private static BufferWriter<WriterAdapter> GetWriter(PipeWriter pipeWriter, int sizeHint)
+            => new BufferWriter<WriterAdapter>(new WriterAdapter(pipeWriter), sizeHint);
 
 
         private struct WriterAdapter : IBufferWriter<byte>
         private struct WriterAdapter : IBufferWriter<byte>
         {
         {
@@ -170,12 +311,29 @@ namespace PlatformBenchmarks
             public ParsingAdapter(BenchmarkApplication requestHandler)
             public ParsingAdapter(BenchmarkApplication requestHandler)
                 => RequestHandler = requestHandler;
                 => RequestHandler = requestHandler;
 
 
-            public void OnHeader(Span<byte> name, Span<byte> value)
+#if NET5_0
+            public void OnStaticIndexedHeader(int index) 
+                => RequestHandler.OnStaticIndexedHeader(index);
+
+            public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
+                => RequestHandler.OnStaticIndexedHeader(index, value);
+
+            public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
                 => RequestHandler.OnHeader(name, value);
                 => RequestHandler.OnHeader(name, value);
 
 
+            public void OnHeadersComplete(bool endStream)
+                => RequestHandler.OnHeadersComplete(endStream);
+
+            public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
+                => RequestHandler.OnStartLine(versionAndMethod, targetPath, startLine);
+#else
+            public void OnHeader(Span<byte> name, Span<byte> value)
+                => RequestHandler.OnHeader(name, value);
+            public void OnHeadersComplete()
+                => RequestHandler.OnHeadersComplete();
             public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
             public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
                 => RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
                 => RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
+#endif
         }
         }
     }
     }
-
-}
+}

+ 15 - 24
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Json.cs

@@ -2,44 +2,35 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System;
 using System;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
+using System.Buffers;
+using System.Text.Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     public partial class BenchmarkApplication
     public partial class BenchmarkApplication
     {
     {
-        private static void Json(PipeWriter pipeWriter)
-        {
-            var writer = GetWriter(pipeWriter);
+        private readonly static uint _jsonPayloadSize = (uint)JsonSerializer.SerializeToUtf8Bytes(new JsonMessage { message = "Hello, World!" }, SerializerOptions).Length;
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
+        private readonly static AsciiString _jsonPreamble =
+            _http11OK +
+            _headerServer + _crlf +
+            _headerContentTypeJson + _crlf +
+            _headerContentLength + _jsonPayloadSize.ToString();
 
 
-            // Server headers
-            writer.Write(_headerServer);
+        private static void Json(ref BufferWriter<WriterAdapter> writer, IBufferWriter<byte> bodyWriter)
+        {
+            writer.Write(_jsonPreamble);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
 
 
-            // Content-Type header
-            writer.Write(_headerContentTypeJson);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
-            var jsonPayload = JsonSerializer.SerializeUnsafe(new JsonMessage() { message = "Hello, World!" });
-            writer.WriteNumeric((uint)jsonPayload.Count);
+            writer.Commit();
 
 
-            // End of headers
-            writer.Write(_eoh);
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(bodyWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(bodyWriter);
 
 
             // Body
             // Body
-            writer.Write(jsonPayload);
-            writer.Commit();
+            JsonSerializer.Serialize<JsonMessage>(utf8JsonWriter, new JsonMessage { message = "Hello, World!" }, SerializerOptions);
         }
         }
     }
     }
 }
 }

+ 12 - 18
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs

@@ -2,9 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.IO.Pipelines;
 using System.IO.Pipelines;
+using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -17,31 +16,26 @@ namespace PlatformBenchmarks
 
 
         private static void OutputMultipleQueries(PipeWriter pipeWriter, World[] rows)
         private static void OutputMultipleQueries(PipeWriter pipeWriter, World[] rows)
         {
         {
-            var writer = GetWriter(pipeWriter);
+            var writer = GetWriter(pipeWriter, sizeHint: 160 * rows.Length); // in reality it's 152 for one
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
+            writer.Write(_dbPreamble);
 
 
-            // Server headers
-            writer.Write(_headerServer);
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
 
 
-            // Content-Type header
-            writer.Write(_headerContentTypeJson);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
-            var jsonPayload = JsonSerializer.SerializeUnsafe(rows);
-            writer.WriteNumeric((uint)jsonPayload.Count);
+            writer.Commit();
 
 
-            // End of headers
-            writer.Write(_eoh);
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
 
 
             // Body
             // Body
-            writer.Write(jsonPayload);
-            writer.Commit();
+            JsonSerializer.Serialize<World[]>(utf8JsonWriter, rows, SerializerOptions);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
         }
         }
     }
     }
 }
 }

+ 8 - 27
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.PlainText.cs

@@ -1,44 +1,25 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // 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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
-using System;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
-
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     public partial class BenchmarkApplication
     public partial class BenchmarkApplication
     {
     {
-        private static void PlainText(PipeWriter pipeWriter)
-        {
-            var writer = GetWriter(pipeWriter);
+        private readonly static AsciiString _plaintextPreamble =
+            _http11OK +
+            _headerServer + _crlf +
+            _headerContentTypeText + _crlf +
+            _headerContentLength + _plainTextBody.Length.ToString();
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
-
-            // Server headers
-            writer.Write(_headerServer);
+        private static void PlainText(ref BufferWriter<WriterAdapter> writer)
+        {
+            writer.Write(_plaintextPreamble);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
 
 
-            // Content-Type header
-            writer.Write(_headerContentTypeText);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
-            writer.WriteNumeric((uint)_plainTextBody.Length);
-
-            // End of headers
-            writer.Write(_eoh);
-
             // Body
             // Body
             writer.Write(_plainTextBody);
             writer.Write(_plainTextBody);
-            writer.Commit();
         }
         }
     }
     }
 }
 }

+ 12 - 18
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs

@@ -2,9 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.IO.Pipelines;
 using System.IO.Pipelines;
+using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -17,31 +16,26 @@ namespace PlatformBenchmarks
 
 
         private static void OutputSingleQuery(PipeWriter pipeWriter, World row)
         private static void OutputSingleQuery(PipeWriter pipeWriter, World row)
         {
         {
-            var writer = GetWriter(pipeWriter);
+            var writer = GetWriter(pipeWriter, sizeHint: 180); // in reality it's 150
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
+            writer.Write(_dbPreamble);
 
 
-            // Server headers
-            writer.Write(_headerServer);
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
 
 
-            // Content-Type header
-            writer.Write(_headerContentTypeJson);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
-            var jsonPayload = JsonSerializer.SerializeUnsafe(row);
-            writer.WriteNumeric((uint)jsonPayload.Count);
+            writer.Commit();
 
 
-            // End of headers
-            writer.Write(_eoh);
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
 
 
             // Body
             // Body
-            writer.Write(jsonPayload);
-            writer.Commit();
+            JsonSerializer.Serialize<World>(utf8JsonWriter, row, SerializerOptions);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
         }
         }
     }
     }
 }
 }

+ 12 - 18
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Updates.cs

@@ -2,9 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.IO.Pipelines;
 using System.IO.Pipelines;
+using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -17,31 +16,26 @@ namespace PlatformBenchmarks
 
 
         private static void OutputUpdates(PipeWriter pipeWriter, World[] rows)
         private static void OutputUpdates(PipeWriter pipeWriter, World[] rows)
         {
         {
-            var writer = GetWriter(pipeWriter);
+            var writer = GetWriter(pipeWriter, sizeHint: 120 * rows.Length); // in reality it's 112 for one
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
+            writer.Write(_dbPreamble);
 
 
-            // Server headers
-            writer.Write(_headerServer);
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
 
 
-            // Content-Type header
-            writer.Write(_headerContentTypeJson);
-
-            // Content-Length header
-            writer.Write(_headerContentLength);
-            var jsonPayload = JsonSerializer.SerializeUnsafe(rows);
-            writer.WriteNumeric((uint)jsonPayload.Count);
+            writer.Commit();
 
 
-            // End of headers
-            writer.Write(_eoh);
+            Utf8JsonWriter utf8JsonWriter = t_writer ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
+            utf8JsonWriter.Reset(pipeWriter);
 
 
             // Body
             // Body
-            writer.Write(jsonPayload);
-            writer.Commit();
+            JsonSerializer.Serialize<World[]>(utf8JsonWriter, rows, SerializerOptions);
+
+            // Content-Length
+            lengthWriter.WriteNumeric((uint)utf8JsonWriter.BytesCommitted);
         }
         }
     }
     }
 }
 }

+ 110 - 88
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs

@@ -3,12 +3,11 @@
 
 
 using System;
 using System;
 using System.Buffers.Text;
 using System.Buffers.Text;
-using System.Collections.Generic;
 using System.IO.Pipelines;
 using System.IO.Pipelines;
-using System.Text.Encodings.Web;
+using System.Text.Json;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using Utf8Json;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -20,15 +19,24 @@ namespace PlatformBenchmarks
         private readonly static AsciiString _crlf = "\r\n";
         private readonly static AsciiString _crlf = "\r\n";
         private readonly static AsciiString _eoh = "\r\n\r\n"; // End Of Headers
         private readonly static AsciiString _eoh = "\r\n\r\n"; // End Of Headers
         private readonly static AsciiString _http11OK = "HTTP/1.1 200 OK\r\n";
         private readonly static AsciiString _http11OK = "HTTP/1.1 200 OK\r\n";
-        private readonly static AsciiString _headerServer = "Server: Custom";
+        private readonly static AsciiString _http11NotFound = "HTTP/1.1 404 Not Found\r\n";
+        private readonly static AsciiString _headerServer = "Server: K";
         private readonly static AsciiString _headerContentLength = "Content-Length: ";
         private readonly static AsciiString _headerContentLength = "Content-Length: ";
-        private readonly static AsciiString _headerContentLengthZero = "Content-Length: 0\r\n";
-        private readonly static AsciiString _headerContentTypeText = "Content-Type: text/plain\r\n";
-        private readonly static AsciiString _headerContentTypeJson = "Content-Type: application/json\r\n";
-        private readonly static AsciiString _headerContentTypeHtml = "Content-Type: text/html; charset=UTF-8\r\n";
+        private readonly static AsciiString _headerContentLengthZero = "Content-Length: 0";
+        private readonly static AsciiString _headerContentTypeText = "Content-Type: text/plain";
+        private readonly static AsciiString _headerContentTypeJson = "Content-Type: application/json";
+        private readonly static AsciiString _headerContentTypeHtml = "Content-Type: text/html; charset=UTF-8";
+
+        private readonly static AsciiString _dbPreamble =
+            _http11OK +
+            _headerServer + _crlf +
+            _headerContentTypeJson + _crlf +
+            _headerContentLength;
 
 
         private readonly static AsciiString _plainTextBody = "Hello, World!";
         private readonly static AsciiString _plainTextBody = "Hello, World!";
 
 
+        private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions();
+
         private readonly static AsciiString _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
         private readonly static AsciiString _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
         private readonly static AsciiString _fortunesRowStart = "<tr><td>";
         private readonly static AsciiString _fortunesRowStart = "<tr><td>";
         private readonly static AsciiString _fortunesColumn = "</td><td>";
         private readonly static AsciiString _fortunesColumn = "</td><td>";
@@ -38,124 +46,137 @@ namespace PlatformBenchmarks
 
 
         public static RawDb Db { get; set; }
         public static RawDb Db { get; set; }
 
 
+        [ThreadStatic]
+        private static Utf8JsonWriter t_writer;
+
         public static class Paths
         public static class Paths
         {
         {
-            public readonly static AsciiString SingleQuery = "/db";
             public readonly static AsciiString Json = "/json";
             public readonly static AsciiString Json = "/json";
-            public readonly static AsciiString Fortunes = "/fortunes";
             public readonly static AsciiString Plaintext = "/plaintext";
             public readonly static AsciiString Plaintext = "/plaintext";
-            public readonly static AsciiString MultipleQueries = "/queries/queries=";
-            public readonly static AsciiString Updates = "/updates/queries=";
+            public readonly static AsciiString SingleQuery = "/db";
+            public readonly static AsciiString Fortunes = "/fortunes";
+            public readonly static AsciiString Updates = "/updates/";
+            public readonly static AsciiString MultipleQueries = "/queries/";
+            public readonly static AsciiString Caching = "/cached-worlds/";
         }
         }
 
 
         private RequestType _requestType;
         private RequestType _requestType;
         private int _queries;
         private int _queries;
 
 
+#if NET5_0
+        public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
+        {
+            _requestType = versionAndMethod.Method == HttpMethod.Get ? GetRequestType(startLine.Slice(targetPath.Offset, targetPath.Length), ref _queries) : RequestType.NotRecognized;
+        }
+#else
         public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
         public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
         {
         {
-            var requestType = RequestType.NotRecognized;
-            if (method == HttpMethod.Get)
-            {
-                var pathLength = path.Length;
-                if (Paths.SingleQuery.Length <= pathLength && path.StartsWith(Paths.SingleQuery))
-                {
-                    requestType = RequestType.SingleQuery;
-                }
-                else if (Paths.Json.Length <= pathLength && path.StartsWith(Paths.Json))
-                {
-                    requestType = RequestType.Json;
-                }
-                else if (Paths.Fortunes.Length <= pathLength && path.StartsWith(Paths.Fortunes))
-                {
-                    requestType = RequestType.Fortunes;
-                }
-                else if (Paths.Plaintext.Length <= pathLength && path.StartsWith(Paths.Plaintext))
-                {
-                    requestType = RequestType.PlainText;
-                }
-                else if (Paths.MultipleQueries.Length <= pathLength && path.StartsWith(Paths.MultipleQueries))
-                {
-                    _queries = ParseQueries(path);
-                    requestType = RequestType.MultipleQueries;
-                }
-                else if (Paths.Updates.Length <= pathLength && path.StartsWith(Paths.Updates))
-                {
-                    _queries = ParseQueries(path);
-                    requestType = RequestType.Updates;
-                }
-            }
-
-            _requestType = requestType;
+            _requestType = method == HttpMethod.Get ? GetRequestType(path, ref _queries) : RequestType.NotRecognized;
         }
         }
+#endif
 
 
-        private static int ParseQueries(Span<byte> path)
+        private RequestType GetRequestType(ReadOnlySpan<byte> path, ref int queries)
         {
         {
-            if (!Utf8Parser.TryParse(path.Slice(Paths.MultipleQueries.Length), out int queries, out _) || queries < 1)
+#if !DATABASE
+            if (path.Length == 10 && path.SequenceEqual(Paths.Plaintext))
             {
             {
-                queries = 1;
+                return RequestType.PlainText;
             }
             }
-            else if (queries > 500)
+            else if (path.Length == 5 && path.SequenceEqual(Paths.Json))
             {
             {
-                queries = 500;
+                return RequestType.Json;
             }
             }
-
-            return queries;
-        }
-
-        public Task ProcessRequestAsync()
-        {
-            var requestType = _requestType;
-            if (requestType == RequestType.PlainText)
+#else
+            if (path.Length == 3 && path[0] == '/' && path[1] == 'd' && path[2] == 'b')
             {
             {
-                PlainText(Writer);
+                return RequestType.SingleQuery;
             }
             }
-            else if (requestType == RequestType.Json)
+            else if (path.Length == 9 && path[1] == 'f' && path.SequenceEqual(Paths.Fortunes))
             {
             {
-                Json(Writer);
+                return RequestType.Fortunes;
             }
             }
-            else if (requestType == RequestType.Fortunes)
+            else if (path.Length >= 15 && path[1] == 'c' && path.StartsWith(Paths.Caching))
             {
             {
-                return Fortunes(Writer);
+                queries = ParseQueries(path.Slice(15));
+                return RequestType.Caching;
             }
             }
-            else if (requestType == RequestType.SingleQuery)
+            else if (path.Length >= 9 && path[1] == 'u' && path.StartsWith(Paths.Updates))
             {
             {
-                return SingleQuery(Writer);
+                queries = ParseQueries(path.Slice(9));
+                return RequestType.Updates;
             }
             }
-            else if (requestType == RequestType.MultipleQueries)
+            else if (path.Length >= 9 && path[1] == 'q' && path.StartsWith(Paths.MultipleQueries))
             {
             {
-                return MultipleQueries(Writer, _queries);
+                queries = ParseQueries(path.Slice(9));
+                return RequestType.MultipleQueries;
             }
             }
-            else if (requestType == RequestType.Updates)
+#endif
+            return RequestType.NotRecognized;
+        }
+
+
+#if !DATABASE
+        private void ProcessRequest(ref BufferWriter<WriterAdapter> writer)
+        {
+            if (_requestType == RequestType.PlainText)
+            {
+                PlainText(ref writer);
+            }
+            else if (_requestType == RequestType.Json)
             {
             {
-                return Updates(Writer, _queries);
+                Json(ref writer, Writer);
             }
             }
             else
             else
             {
             {
-                Default(Writer);
+                Default(ref writer);
             }
             }
-
-            return Task.CompletedTask;
         }
         }
+#else
 
 
-        private static void Default(PipeWriter pipeWriter)
+        private static int ParseQueries(ReadOnlySpan<byte> parameter)
         {
         {
-            var writer = GetWriter(pipeWriter);
+            if (!Utf8Parser.TryParse(parameter, out int queries, out _) || queries < 1)
+            {
+                queries = 1;
+            }
+            else if (queries > 500)
+            {
+                queries = 500;
+            }
 
 
-            // HTTP 1.1 OK
-            writer.Write(_http11OK);
+            return queries;
+        }
 
 
-            // Server headers
-            writer.Write(_headerServer);
+        private Task ProcessRequestAsync() => _requestType switch
+        {
+            RequestType.Fortunes => Fortunes(Writer),
+            RequestType.SingleQuery => SingleQuery(Writer),
+            RequestType.Caching => Caching(Writer, _queries),
+            RequestType.Updates => Updates(Writer, _queries),
+            RequestType.MultipleQueries => MultipleQueries(Writer, _queries),
+            _ => Default(Writer)
+        };
+
+        private static Task Default(PipeWriter pipeWriter)
+        {
+            var writer = GetWriter(pipeWriter, sizeHint: _defaultPreamble.Length + DateHeader.HeaderBytes.Length);
+            Default(ref writer);
+            writer.Commit();
+            return Task.CompletedTask;
+        }
+#endif
+        private readonly static AsciiString _defaultPreamble =
+            _http11NotFound +
+            _headerServer + _crlf +
+            _headerContentTypeText + _crlf +
+            _headerContentLengthZero;
+
+        private static void Default(ref BufferWriter<WriterAdapter> writer)
+        {
+            writer.Write(_defaultPreamble);
 
 
             // Date header
             // Date header
             writer.Write(DateHeader.HeaderBytes);
             writer.Write(DateHeader.HeaderBytes);
-
-            // Content-Length 0
-            writer.Write(_headerContentLengthZero);
-
-            // End of headers
-            writer.Write(_crlf);
-            writer.Commit();
         }
         }
 
 
         private enum RequestType
         private enum RequestType
@@ -165,8 +186,9 @@ namespace PlatformBenchmarks
             Json,
             Json,
             Fortunes,
             Fortunes,
             SingleQuery,
             SingleQuery,
-            MultipleQueries,
-            Updates
+            Caching,
+            Updates,
+            MultipleQueries
         }
         }
     }
     }
-}
+}

+ 18 - 34
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs

@@ -3,10 +3,11 @@
 
 
 using System;
 using System;
 using System.Net;
 using System.Net;
-using Microsoft.AspNetCore;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
+using System.IO.Pipelines;
+using System.Runtime.InteropServices;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -16,43 +17,26 @@ namespace PlatformBenchmarks
         {
         {
             builder.UseConfiguration(configuration);
             builder.UseConfiguration(configuration);
 
 
-            // Handle the transport type
-            var webHost = builder.GetSetting("KestrelTransport");
-
-            // Handle the thread count
-            var threadCountRaw = builder.GetSetting("threadCount");
-            int? theadCount = null;
-
-            if (!string.IsNullOrEmpty(threadCountRaw) && 
-                Int32.TryParse(threadCountRaw, out var value))
-            {
-                theadCount = value;
-            }
-
-            if (string.Equals(webHost, "Libuv", StringComparison.OrdinalIgnoreCase))
+            builder.UseSockets(options =>
             {
             {
-                builder.UseLibuv(options =>
+                if (int.TryParse(builder.GetSetting("threadCount"), out int threadCount))
                 {
                 {
-                    if (theadCount.HasValue)
-                    {
-                        options.ThreadCount = theadCount.Value;
-                    }
-                });
-            }
-            else if (string.Equals(webHost, "Sockets", StringComparison.OrdinalIgnoreCase))
-            {
-                builder.UseSockets(options =>
+                    options.IOQueueCount = threadCount;
+                }
+#if NET5_0
+                options.WaitForDataBeforeAllocatingBuffer = false;
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                 {
                 {
-                    if (theadCount.HasValue)
-                    {
-                        options.IOQueueCount = theadCount.Value;
-                    }
-                });
-            }
+                    options.UnsafePreferInlineScheduling = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS") == "1";
+                }
+
+                Console.WriteLine($"Options: WaitForData={options.WaitForDataBeforeAllocatingBuffer}, PreferInlineScheduling={options.UnsafePreferInlineScheduling}, IOQueue={options.IOQueueCount}");
+#endif
+            });
 
 
             return builder;
             return builder;
         }
         }
-        
+
         public static IPEndPoint CreateIPEndPoint(this IConfiguration config)
         public static IPEndPoint CreateIPEndPoint(this IConfiguration config)
         {
         {
             var url = config["server.urls"] ?? config["urls"];
             var url = config["server.urls"] ?? config["urls"];
@@ -62,7 +46,7 @@ namespace PlatformBenchmarks
                 return new IPEndPoint(IPAddress.Loopback, 8080);
                 return new IPEndPoint(IPAddress.Loopback, 8080);
             }
             }
 
 
-            var address = ServerAddress.FromUrl(url);
+            var address = BindingAddress.Parse(url);
 
 
             IPAddress ip;
             IPAddress ip;
 
 

+ 63 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BufferExtensions.cs

@@ -0,0 +1,63 @@
+// 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.Buffers;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace PlatformBenchmarks
+{
+    // Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs
+    // However methods accept T : struct, IBufferWriter<byte> rather than PipeWriter.
+    // This allows a struct wrapper to turn CountingBufferWriter into a non-shared generic,
+    // while still offering the WriteNumeric extension.
+
+    public static class BufferExtensions
+    {
+        private const int _maxULongByteLength = 20;
+
+        [ThreadStatic]
+        private static byte[] _numericBytesScratch;
+
+        internal static void WriteUtf8String<T>(ref this BufferWriter<T> buffer, string text)
+             where T : struct, IBufferWriter<byte>
+        {
+            var byteCount = Encoding.UTF8.GetByteCount(text);
+            buffer.Ensure(byteCount);
+            byteCount = Encoding.UTF8.GetBytes(text.AsSpan(), buffer.Span);
+            buffer.Advance(byteCount);
+        }
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        internal static void WriteNumericMultiWrite<T>(ref this BufferWriter<T> buffer, uint number)
+             where T : IBufferWriter<byte>
+        {
+            const byte AsciiDigitStart = (byte)'0';
+
+            var value = number;
+            var position = _maxULongByteLength;
+            var byteBuffer = NumericBytesScratch;
+            do
+            {
+                // Consider using Math.DivRem() if available
+                var quotient = value / 10;
+                byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
+                value = quotient;
+            }
+            while (value != 0);
+
+            var length = _maxULongByteLength - position;
+            buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
+        }
+
+        private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static byte[] CreateNumericBytesScratch()
+        {
+            var bytes = new byte[_maxULongByteLength];
+            _numericBytesScratch = bytes;
+            return bytes;
+        }
+    }
+}

+ 52 - 4
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferWriter.cs → frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BufferWriter.cs

@@ -13,14 +13,17 @@ namespace PlatformBenchmarks
         private Span<byte> _span;
         private Span<byte> _span;
         private int _buffered;
         private int _buffered;
 
 
-        public BufferWriter(T output)
+        public BufferWriter(T output, int sizeHint)
         {
         {
             _buffered = 0;
             _buffered = 0;
             _output = output;
             _output = output;
-            _span = output.GetSpan();
+            _span = output.GetSpan(sizeHint);
         }
         }
 
 
         public Span<byte> Span => _span;
         public Span<byte> Span => _span;
+
+        public T Output => _output;
+
         public int Buffered => _buffered;
         public int Buffered => _buffered;
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -72,8 +75,7 @@ namespace PlatformBenchmarks
                 Commit();
                 Commit();
             }
             }
 
 
-            _output.GetMemory(count);
-            _span = _output.GetSpan();
+            _span = _output.GetSpan(count);
         }
         }
 
 
         private void WriteMultiBuffer(ReadOnlySpan<byte> source)
         private void WriteMultiBuffer(ReadOnlySpan<byte> source)
@@ -91,5 +93,51 @@ namespace PlatformBenchmarks
                 Advance(writable);
                 Advance(writable);
             }
             }
         }
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void WriteNumeric(uint number)
+        {
+            const byte AsciiDigitStart = (byte)'0';
+
+            var span = this.Span;
+
+            // Fast path, try copying to the available memory directly
+            var advanceBy = 0;
+            if (span.Length >= 3)
+            {
+                if (number < 10)
+                {
+                    span[0] = (byte)(number + AsciiDigitStart);
+                    advanceBy = 1;
+                }
+                else if (number < 100)
+                {
+                    var tens = (byte)((number * 205u) >> 11); // div10, valid to 1028
+
+                    span[0] = (byte)(tens + AsciiDigitStart);
+                    span[1] = (byte)(number - (tens * 10) + AsciiDigitStart);
+                    advanceBy = 2;
+                }
+                else if (number < 1000)
+                {
+                    var digit0 = (byte)((number * 41u) >> 12); // div100, valid to 1098
+                    var digits01 = (byte)((number * 205u) >> 11); // div10, valid to 1028
+
+                    span[0] = (byte)(digit0 + AsciiDigitStart);
+                    span[1] = (byte)(digits01 - (digit0 * 10) + AsciiDigitStart);
+                    span[2] = (byte)(number - (digits01 * 10) + AsciiDigitStart);
+                    advanceBy = 3;
+                }
+            }
+
+            if (advanceBy > 0)
+            {
+                Advance(advanceBy);
+            }
+            else
+            {
+                BufferExtensions.WriteNumericMultiWrite(ref this, number);
+            }
+        }
     }
     }
 }
 }

+ 27 - 40
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs

@@ -1,57 +1,44 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // 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. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 
-using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
-using System.Runtime.CompilerServices;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     internal class BatchUpdateString
     internal class BatchUpdateString
     {
     {
         private const int MaxBatch = 500;
         private const int MaxBatch = 500;
-        private static string[] _queries = new string[MaxBatch];
-
-        public static IList<BatchUpdateString> Strings { get; } =
-            Enumerable.Range(0, MaxBatch)
-                      .Select(i => new BatchUpdateString
-                      {
-                          Id = $"Id_{i}",
-                          Random = $"Random_{i}",
-                          BatchSize = i
-                      }).ToArray();
-
-        private int BatchSize { get; set; }
-        public string Id { get; set; }
-        public string Random { get; set; }
-        public string UpdateQuery => _queries[BatchSize] ?? CreateQuery(BatchSize);
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private string CreateQuery(int batchSize)
+
+        public static DatabaseServer DatabaseServer;
+
+        internal static readonly string[] Ids = Enumerable.Range(0, MaxBatch).Select(i => $"@Id_{i}").ToArray();
+        internal static readonly string[] Randoms = Enumerable.Range(0, MaxBatch).Select(i => $"@Random_{i}").ToArray();
+
+        private static string[] _queries = new string[MaxBatch + 1];
+
+        public static string Query(int batchSize)
         {
         {
-            var sb = StringBuilderCache.Acquire();
-            foreach (var q in Enumerable.Range(0, batchSize + 1)
-                .Select(i => $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"))
+            if (_queries[batchSize] != null)
             {
             {
-                sb.Append(q);
+                return _queries[batchSize];
             }
             }
-            var query = sb.ToString();
-            _queries[batchSize] = query;
-            return query;
-        }
 
 
-        public static void Initalize()
-        {
-            Observe(Strings[0].UpdateQuery);
-            Observe(Strings[4].UpdateQuery);
-            Observe(Strings[9].UpdateQuery);
-            Observe(Strings[14].UpdateQuery);
-            Observe(Strings[19].UpdateQuery);
-        }
+            var lastIndex = batchSize - 1;
 
 
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static void Observe(string query)
-        {
+            var sb = StringBuilderCache.Acquire();
+
+            if (DatabaseServer == DatabaseServer.PostgreSql)
+            {
+                sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
+                Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@Id_{i}, @Random_{i}), "));
+                sb.Append($"(@Id_{lastIndex}, @Random_{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
+            }
+            else
+            {
+                Enumerable.Range(0, batchSize).ToList().ForEach(i => sb.Append($"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"));
+            }
+
+            return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
         }
         }
     }
     }
-}
+}

+ 17 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/CachedWorld.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.
+
+using System.Runtime.InteropServices;
+
+namespace PlatformBenchmarks
+{
+    public class CachedWorld
+    {
+        public int Id { get; set; }
+
+        public int RandomNumber { get; set; }
+
+        public static implicit operator World(CachedWorld world) => new World { Id = world.Id, RandomNumber = world.RandomNumber };
+        public static implicit operator CachedWorld(World world) => new CachedWorld { Id = world.Id, RandomNumber = world.RandomNumber };
+    }
+}

+ 12 - 12
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Fortune.cs

@@ -5,21 +5,21 @@ using System;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
-    public class Fortune : IComparable<Fortune>, IComparable
+    public readonly struct Fortune : IComparable<Fortune>, IComparable
     {
     {
-        public int Id { get; set; }
-
-        public string Message { get; set; }
-        
-        public int CompareTo(object obj)
+        public Fortune(int id, string message)
         {
         {
-            return CompareTo((Fortune)obj);
+            Id = id;
+            Message = message;
         }
         }
 
 
-        public int CompareTo(Fortune other)
-        {
-            // Performance critical, using culture insensitive comparison
-            return String.CompareOrdinal(Message, other.Message);
-        }
+        public int Id { get; }
+
+        public string Message { get; }
+
+        public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
+
+        // Performance critical, using culture insensitive comparison
+        public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
     }
     }
 }
 }

+ 172 - 78
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs

@@ -3,166 +3,260 @@
 
 
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Data;
-using System.Data.Common;
+using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Microsoft.Extensions.Caching.Memory;
+using Npgsql;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
     public class RawDb
     public class RawDb
     {
     {
-        private static readonly Comparison<World> WorldSortComparison = (a, b) => a.Id.CompareTo(b.Id);
-
         private readonly ConcurrentRandom _random;
         private readonly ConcurrentRandom _random;
-        private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
         private readonly string _connectionString;
+        private readonly MemoryCache _cache = new MemoryCache(
+            new MemoryCacheOptions()
+            {
+                ExpirationScanFrequency = TimeSpan.FromMinutes(60)
+            });
 
 
-        public RawDb(ConcurrentRandom random, DbProviderFactory dbProviderFactory, AppSettings appSettings)
+        public RawDb(ConcurrentRandom random, AppSettings appSettings)
         {
         {
             _random = random;
             _random = random;
-            _dbProviderFactory = dbProviderFactory;
             _connectionString = appSettings.ConnectionString;
             _connectionString = appSettings.ConnectionString;
         }
         }
 
 
         public async Task<World> LoadSingleQueryRow()
         public async Task<World> LoadSingleQueryRow()
         {
         {
-            using (var db = _dbProviderFactory.CreateConnection())
+            using (var db = new NpgsqlConnection(_connectionString))
             {
             {
-                db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
 
 
-                using (var cmd = CreateReadCommand(db))
+                var (cmd, _) = CreateReadCommand(db);
+                using (cmd)
                 {
                 {
-                    return await ReadSingleRow(db, cmd);
+                    return await ReadSingleRow(cmd);
                 }
                 }
             }
             }
         }
         }
 
 
-        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
         {
-            using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+            var result = new World[count];
+
+            using (var db = new NpgsqlConnection(_connectionString))
             {
             {
-                await rdr.ReadAsync();
+                await db.OpenAsync();
 
 
-                return new World
+                var (cmd, idParameter) = CreateReadCommand(db);
+                using (cmd)
                 {
                 {
-                    Id = rdr.GetInt32(0),
-                    RandomNumber = rdr.GetInt32(1)
-                };
+                    for (int i = 0; i < result.Length; i++)
+                    {
+                        result[i] = await ReadSingleRow(cmd);
+                        idParameter.TypedValue = _random.Next(1, 10001);
+                    }
+                }
             }
             }
-        }
 
 
-        DbCommand CreateReadCommand(DbConnection connection)
-        {
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id";
-            var id = cmd.CreateParameter();
-            id.ParameterName = "@Id";
-            id.DbType = DbType.Int32;
-            id.Value = _random.Next(1, 10001);
-            cmd.Parameters.Add(id);
-
-            return cmd;
+            return result;
         }
         }
 
 
-        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        public Task<World[]> LoadCachedQueries(int count)
         {
         {
-            using (var db = _dbProviderFactory.CreateConnection())
+            var result = new World[count];
+            var cacheKeys = _cacheKeys;
+            var cache = _cache;
+            var random = _random;
+            for (var i = 0; i < result.Length; i++)
             {
             {
-                db.ConnectionString = _connectionString;
-                await db.OpenAsync();
-                return await LoadMultipleRows(count, db);
+                var id = random.Next(1, 10001);
+                var key = cacheKeys[id];
+                var data = cache.Get<CachedWorld>(key);
+
+                if (data != null)
+                {
+                    result[i] = data;
+                }
+                else
+                {
+                    return LoadUncachedQueries(id, i, count, this, result);
+                }
             }
             }
 
 
+            return Task.FromResult(result);
+
+            static async Task<World[]> LoadUncachedQueries(int id, int i, int count, RawDb rawdb, World[] result)
+            {
+                using (var db = new NpgsqlConnection(rawdb._connectionString))
+                {
+                    await db.OpenAsync();
+
+                    var (cmd, idParameter) = rawdb.CreateReadCommand(db);
+                    using (cmd)
+                    {
+                        Func<ICacheEntry, Task<CachedWorld>> create = async (entry) => 
+                        {
+                            return await rawdb.ReadSingleRow(cmd);
+                        };
+
+                        var cacheKeys = _cacheKeys;
+                        var key = cacheKeys[id];
+
+                        idParameter.TypedValue = id;
+
+                        for (; i < result.Length; i++)
+                        {
+                            var data = await rawdb._cache.GetOrCreateAsync<CachedWorld>(key, create);
+                            result[i] = data;
+
+                            id = rawdb._random.Next(1, 10001);
+                            idParameter.TypedValue = id;
+                            key = cacheKeys[id];
+                        }
+                    }
+                }
+
+                return result;
+            }
         }
         }
 
 
-        private async Task<World[]> LoadMultipleRows(int count, DbConnection db)
+        public async Task PopulateCache()
         {
         {
-            using (var cmd = CreateReadCommand(db))
+            using (var db = new NpgsqlConnection(_connectionString))
             {
             {
-                cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                await db.OpenAsync();
 
 
-                var result = new World[count];
-                for (int i = 0; i < result.Length; i++)
+                var (cmd, idParameter) = CreateReadCommand(db);
+                using (cmd)
                 {
                 {
-                    result[i] = await ReadSingleRow(db, cmd);
-                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    var cacheKeys = _cacheKeys;
+                    var cache = _cache;
+                    for (var i = 1; i < 10001; i++)
+                    {
+                        idParameter.TypedValue = i;
+                        cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(cmd));
+                    }
                 }
                 }
-                return result;
             }
             }
+
+            Console.WriteLine("Caching Populated");
         }
         }
 
 
         public async Task<World[]> LoadMultipleUpdatesRows(int count)
         public async Task<World[]> LoadMultipleUpdatesRows(int count)
         {
         {
-            using (var db = _dbProviderFactory.CreateConnection())
+            var results = new World[count];
+
+            using (var db = new NpgsqlConnection(_connectionString))
             {
             {
-                db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
 
 
-                var results = await LoadMultipleRows(count, db);
-
-                // Postgres has problems with deadlocks when these aren't sorted
-                Array.Sort<World>(results, WorldSortComparison);
-
-                using (var updateCmd = db.CreateCommand())
+                var (queryCmd, queryParameter) = CreateReadCommand(db);
+                using (queryCmd)
                 {
                 {
                     for (int i = 0; i < results.Length; i++)
                     for (int i = 0; i < results.Length; i++)
                     {
                     {
-                        var strings = BatchUpdateString.Strings[i];
-
-                        var id = updateCmd.CreateParameter();
-                        id.ParameterName = strings.Id;
-                        id.DbType = DbType.Int32;
-                        updateCmd.Parameters.Add(id);
+                        results[i] = await ReadSingleRow(queryCmd);
+                        queryParameter.TypedValue = _random.Next(1, 10001);
+                    }
+                }
 
 
-                        var random = updateCmd.CreateParameter();
-                        random.ParameterName = strings.Random;
-                        random.DbType = DbType.Int32;
-                        updateCmd.Parameters.Add(random);
+                using (var updateCmd = new NpgsqlCommand(BatchUpdateString.Query(count), db))
+                {
+                    var ids = BatchUpdateString.Ids;
+                    var randoms = BatchUpdateString.Randoms;
 
 
+                    for (int i = 0; i < results.Length; i++)
+                    {
                         var randomNumber = _random.Next(1, 10001);
                         var randomNumber = _random.Next(1, 10001);
-                        id.Value = results[i].Id;
-                        random.Value = randomNumber;
+
+                        updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: ids[i], value: results[i].Id));
+                        updateCmd.Parameters.Add(new NpgsqlParameter<int>(parameterName: randoms[i], value: randomNumber));
+
                         results[i].RandomNumber = randomNumber;
                         results[i].RandomNumber = randomNumber;
                     }
                     }
 
 
-                    updateCmd.CommandText = BatchUpdateString.Strings[count - 1].UpdateQuery;
                     await updateCmd.ExecuteNonQueryAsync();
                     await updateCmd.ExecuteNonQueryAsync();
-
-                    return results;
                 }
                 }
             }
             }
+
+            return results;
         }
         }
 
 
         public async Task<List<Fortune>> LoadFortunesRows()
         public async Task<List<Fortune>> LoadFortunesRows()
         {
         {
-            var result = new List<Fortune>();
+            var result = new List<Fortune>(20);
 
 
-            using (var db = _dbProviderFactory.CreateConnection())
-            using (var cmd = db.CreateCommand())
+            using (var db = new NpgsqlConnection(_connectionString))
             {
             {
-                cmd.CommandText = "SELECT id, message FROM fortune";
-
-                db.ConnectionString = _connectionString;
                 await db.OpenAsync();
                 await db.OpenAsync();
 
 
-                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
+                using (var cmd = new NpgsqlCommand("SELECT id, message FROM fortune", db))
+                using (var rdr = await cmd.ExecuteReaderAsync())
                 {
                 {
                     while (await rdr.ReadAsync())
                     while (await rdr.ReadAsync())
                     {
                     {
                         result.Add(new Fortune
                         result.Add(new Fortune
-                        {
-                            Id = rdr.GetInt32(0),
-                            Message = rdr.GetString(1)
-                        });
+                        (
+                            id:rdr.GetInt32(0),
+                            message: rdr.GetString(1)
+                        ));
                     }
                     }
                 }
                 }
             }
             }
 
 
-            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
             result.Sort();
             result.Sort();
 
 
             return result;
             return result;
         }
         }
+
+        private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
+        {
+            var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = @Id", connection);
+            var parameter = new NpgsqlParameter<int>(parameterName: "@Id", value: _random.Next(1, 10001));
+
+            cmd.Parameters.Add(parameter);
+
+            return (cmd, parameter);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private async Task<World> ReadSingleRow(NpgsqlCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
+            }
+        }
+
+        private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
+
+        public sealed class CacheKey : IEquatable<CacheKey>
+        {
+            private readonly int _value;
+
+            public CacheKey(int value)
+                => _value = value;
+
+            public bool Equals(CacheKey key)
+                => key._value == _value;
+
+            public override bool Equals(object obj) 
+                => ReferenceEquals(obj, this);
+
+            public override int GetHashCode()
+                => _value;
+
+            public override string ToString()
+                => _value.ToString();
+        }
     }
     }
 }
 }

+ 13 - 8
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/DateHeader.cs → frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/DateHeader.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System;
 using System;
@@ -6,14 +6,13 @@ using System.Buffers.Text;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 
 
-namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
+namespace PlatformBenchmarks
 {
 {
     /// <summary>
     /// <summary>
     /// Manages the generation of the date header value.
     /// Manages the generation of the date header value.
     /// </summary>
     /// </summary>
-    internal static class DateHeader 
+    internal static class DateHeader
     {
     {
         const int prefixLength = 8; // "\r\nDate: ".Length
         const int prefixLength = 8; // "\r\nDate: ".Length
         const int dateTimeRLength = 29; // Wed, 14 Mar 2018 14:20:00 GMT
         const int dateTimeRLength = 29; // Wed, 14 Mar 2018 14:20:00 GMT
@@ -24,18 +23,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             SetDateValues(DateTimeOffset.UtcNow);
             SetDateValues(DateTimeOffset.UtcNow);
         }, null, 1000, 1000);
         }, null, 1000, 1000);
 
 
-        private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + suffixLength];
-        private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + suffixLength];
+        private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + 2 * suffixLength];
+        private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + 2 * suffixLength];
 
 
         static DateHeader()
         static DateHeader()
         {
         {
             var utf8 = Encoding.ASCII.GetBytes("\r\nDate: ").AsSpan();
             var utf8 = Encoding.ASCII.GetBytes("\r\nDate: ").AsSpan();
+
             utf8.CopyTo(s_headerBytesMaster);
             utf8.CopyTo(s_headerBytesMaster);
             utf8.CopyTo(s_headerBytesScratch);
             utf8.CopyTo(s_headerBytesScratch);
             s_headerBytesMaster[suffixIndex] = (byte)'\r';
             s_headerBytesMaster[suffixIndex] = (byte)'\r';
             s_headerBytesMaster[suffixIndex + 1] = (byte)'\n';
             s_headerBytesMaster[suffixIndex + 1] = (byte)'\n';
+            s_headerBytesMaster[suffixIndex + 2] = (byte)'\r';
+            s_headerBytesMaster[suffixIndex + 3] = (byte)'\n';
             s_headerBytesScratch[suffixIndex] = (byte)'\r';
             s_headerBytesScratch[suffixIndex] = (byte)'\r';
             s_headerBytesScratch[suffixIndex + 1] = (byte)'\n';
             s_headerBytesScratch[suffixIndex + 1] = (byte)'\n';
+            s_headerBytesScratch[suffixIndex + 2] = (byte)'\r';
+            s_headerBytesScratch[suffixIndex + 3] = (byte)'\n';
+
             SetDateValues(DateTimeOffset.UtcNow);
             SetDateValues(DateTimeOffset.UtcNow);
             SyncDateTimer();
             SyncDateTimer();
         }
         }
@@ -48,7 +53,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         {
         {
             lock (s_headerBytesScratch)
             lock (s_headerBytesScratch)
             {
             {
-                if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan(prefixLength), out int written, 'R'))
+                if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan(prefixLength), out var written, 'R'))
                 {
                 {
                     throw new Exception("date time format failed");
                     throw new Exception("date time format failed");
                 }
                 }
@@ -59,4 +64,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
             }
         }
         }
     }
     }
-}
+}

+ 0 - 1
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/IHttpConnection.cs

@@ -12,6 +12,5 @@ namespace PlatformBenchmarks
         PipeReader Reader { get; set; }
         PipeReader Reader { get; set; }
         PipeWriter Writer { get; set; }
         PipeWriter Writer { get; set; }
         Task ExecuteAsync();
         Task ExecuteAsync();
-        ValueTask OnReadCompletedAsync();
     }
     }
 }
 }

+ 9 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Linker.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- See: https://github.com/mono/linker/blob/master/docs/data-formats.md -->
+<linker>
+	<assembly fullname="Microsoft.AspNetCore.Server.Kestrel.Core">
+		<type fullname="Microsoft.AspNetCore.Server.Kestrel.Core.Internal.CertificateConfig" />
+	</assembly>
+  <assembly fullname="Microsoft.AspNetCore.Hosting.Abstractions" />
+  <assembly fullname="System.Threading.Channels" />
+</linker>

+ 8 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/NuGet.Config

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <packageSources>
+    <clear />
+    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
+    <add key="dotnet5" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json" />
+  </packageSources>
+</configuration>

+ 57 - 12
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/PlatformBenchmarks.csproj

@@ -1,23 +1,68 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net471</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
-    <LangVersion>7.3</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
+    <EnableTargetingPackDownload>false</EnableTargetingPackDownload>
+    <PublishTrimmed>true</PublishTrimmed>
+    <_TrimmerDefaultAction>link</_TrimmerDefaultAction>
+  </PropertyGroup>
+  
+  <PropertyGroup>
+    <DefineConstants Condition=" '$(IsDatabase)' == 'true' ">$(DefineConstants);DATABASE</DefineConstants>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0"/>
-    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.3" />
-    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="2.1.3" />
-    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.1.3" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.1" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
-    <PackageReference Include="Utf8Json" Version="1.3.7" />
-    <PackageReference Include="Npgsql" Version="4.1.3.1" />
-    <PackageReference Include="MySqlConnector" Version="0.62.0" />
+    <TrimmerRootDescriptor Include="Linker.xml" />
   </ItemGroup>
   </ItemGroup>
   
   
+  <ItemGroup>
+      <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Npgsql" Version="5.0.0-alpha1" />
+    <PackageReference Include="System.IO.Pipelines" Version="5.0.0-rc.1.20451.14" />
+  </ItemGroup>
+
+  <!-- Redirect "dotnet publish" to "mono-rp" folder-->
+  <Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
+    <PropertyGroup>
+      <MonoRuntimeVer>5.0.0-rc.2.20474.8</MonoRuntimeVer>
+    </PropertyGroup>
+    <DownloadFile SourceUrl="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/flat2/microsoft.netcore.app.runtime.mono.llvm.aot.$(RuntimeIdentifier)/$(MonoRuntimeVer)/microsoft.netcore.app.runtime.mono.llvm.aot.$(RuntimeIdentifier).$(MonoRuntimeVer).nupkg" DestinationFolder="$(MSBuildThisFileDirectory)" DestinationFileName="mono-rp.zip">
+    </DownloadFile>
+    <Unzip SourceFiles="mono-rp.zip" DestinationFolder="mono-rp" />
+    <ItemGroup>
+      <RuntimePack>
+        <PackageDirectory>mono-rp</PackageDirectory>
+      </RuntimePack>
+    </ItemGroup>
+    <Message Text="New PackageDirectory: %(RuntimePack.PackageDirectory)" Importance="high" />
+  </Target>
+
+  <Target Name="AspNetCore" AfterTargets="ComputeResolvedFilesToPublishList">
+    <ItemGroup>
+      <AspNetCoreFiles Include="$(MSBuildBinPath)/../../shared/Microsoft.AspNetCore.App/**/*.dll" />
+    </ItemGroup>
+    <ItemGroup>
+      <ExtraFiles Include="%(RuntimePack.PackageDirectory)/runtimes/$(RuntimeIdentifier)/lib/$(TargetFramework)/*.dll" />
+    </ItemGroup>
+
+    <Copy SourceFiles="@(AspNetCoreFiles)" DestinationFolder="$(PublishDir)" />
+    <Copy SourceFiles="@(ExtraFiles)" DestinationFolder="$(PublishDir)" />
+  </Target>
+  
+  <!-- TODO: remove once https://github.com/dotnet/runtime/pull/42729 is merged and nugets are updated -->
+  <Target Name="hack" AfterTargets="Publish">
+    <ItemGroup>
+      <HostingFiles Include="$(MSBuildBinPath)/../../host/fxr/**/libhostfxr.*" />
+      <PolicyFiles Include="$(MSBuildBinPath)/../../shared/Microsoft.NETCore.App/**/libhostpolicy.*" />
+    </ItemGroup>
+
+    <Copy SourceFiles="@(HostingFiles)" DestinationFolder="$(PublishDir)" />
+    <Copy SourceFiles="@(PolicyFiles)" DestinationFolder="$(PublishDir)" />
+  </Target>
 </Project>
 </Project>

+ 30 - 10
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs

@@ -3,11 +3,13 @@
 
 
 using System;
 using System;
 using System.Net;
 using System.Net;
+using System.Reflection;
+using System.Threading.Tasks;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
+#if DATABASE
 using Npgsql;
 using Npgsql;
-using MySql.Data.MySqlClient;
+#endif
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -15,47 +17,65 @@ namespace PlatformBenchmarks
     {
     {
         public static string[] Args;
         public static string[] Args;
 
 
-        public static void Main(string[] args)
+        public static async Task Main(string[] args)
         {
         {
             Args = args;
             Args = args;
 
 
             Console.WriteLine(BenchmarkApplication.ApplicationName);
             Console.WriteLine(BenchmarkApplication.ApplicationName);
+            bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null;
+            Console.WriteLine("Runtime " + (isMono ? "Mono" : "CoreCLR"));
+            Console.WriteLine(typeof(object).Assembly.FullName);
+            Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly());
+            Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
+#if !DATABASE
             Console.WriteLine(BenchmarkApplication.Paths.Plaintext);
             Console.WriteLine(BenchmarkApplication.Paths.Plaintext);
             Console.WriteLine(BenchmarkApplication.Paths.Json);
             Console.WriteLine(BenchmarkApplication.Paths.Json);
+#else
             Console.WriteLine(BenchmarkApplication.Paths.Fortunes);
             Console.WriteLine(BenchmarkApplication.Paths.Fortunes);
             Console.WriteLine(BenchmarkApplication.Paths.SingleQuery);
             Console.WriteLine(BenchmarkApplication.Paths.SingleQuery);
+            Console.WriteLine(BenchmarkApplication.Paths.Updates);
+            Console.WriteLine(BenchmarkApplication.Paths.MultipleQueries);
+#endif
             DateHeader.SyncDateTimer();
             DateHeader.SyncDateTimer();
 
 
-            BatchUpdateString.Initalize();
-
-            BuildWebHost(args).Run();
+            var host = BuildWebHost(args);
+            var config = (IConfiguration)host.Services.GetService(typeof(IConfiguration));
+            BatchUpdateString.DatabaseServer = config.Get<AppSettings>().Database;
+#if DATABASE
+            await BenchmarkApplication.Db.PopulateCache();
+#endif
+            await host.RunAsync();
         }
         }
 
 
         public static IWebHost BuildWebHost(string[] args)
         public static IWebHost BuildWebHost(string[] args)
         {
         {
             var config = new ConfigurationBuilder()
             var config = new ConfigurationBuilder()
                 .AddJsonFile("appsettings.json")
                 .AddJsonFile("appsettings.json")
+                .AddEnvironmentVariables()
                 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
                 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
                 .AddCommandLine(args)
                 .AddCommandLine(args)
                 .Build();
                 .Build();
 
 
             var appSettings = config.Get<AppSettings>();
             var appSettings = config.Get<AppSettings>();
+#if DATABASE
             Console.WriteLine($"Database: {appSettings.Database}");
             Console.WriteLine($"Database: {appSettings.Database}");
+            Console.WriteLine($"ConnectionString: {appSettings.ConnectionString}");
 
 
             if (appSettings.Database == DatabaseServer.PostgreSql)
             if (appSettings.Database == DatabaseServer.PostgreSql)
             {
             {
-                BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), NpgsqlFactory.Instance, appSettings);
+                BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), appSettings);
             }
             }
-            else if (appSettings.Database == DatabaseServer.MySql)
+            else
             {
             {
-                BenchmarkApplication.Db = new RawDb(new ConcurrentRandom(), MySqlClientFactory.Instance, appSettings);
+                throw new NotSupportedException($"{appSettings.Database} is not supported");
             }
             }
+#endif
 
 
             var host = new WebHostBuilder()
             var host = new WebHostBuilder()
                 .UseBenchmarksConfiguration(config)
                 .UseBenchmarksConfiguration(config)
                 .UseKestrel((context, options) =>
                 .UseKestrel((context, options) =>
                 {
                 {
-                    IPEndPoint endPoint = context.Configuration.CreateIPEndPoint();
+                    var endPoint = context.Configuration.CreateIPEndPoint();
 
 
                     options.Listen(endPoint, builder =>
                     options.Listen(endPoint, builder =>
                     {
                     {

+ 1 - 1
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/StringBuilderCache.cs → frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/StringBuilderCache.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
+// 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. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 
 using System;
 using System;

+ 0 - 106
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsNumeric.cs

@@ -1,106 +0,0 @@
-// 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.Buffers;
-using System.Buffers.Text;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace PlatformBenchmarks
-{
-    // Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs
-    // However methods accept T : struct, IBufferWriter<byte> rather than PipeWriter.
-    // This allows a struct wrapper to turn CountingBufferWriter into a non-shared generic,
-    // while still offering the WriteNumeric extension.
-
-    public static class BufferExtensionsNumeric
-    {
-        private const int _maxULongByteLength = 20;
-
-        [ThreadStatic]
-        private static byte[] _numericBytesScratch;
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static unsafe void WriteNumeric<T>(ref this BufferWriter<T> buffer, uint number)
-             where T : struct, IBufferWriter<byte>
-        {
-            const byte AsciiDigitStart = (byte)'0';
-
-            var span = buffer.Span;
-            var bytesLeftInBlock = span.Length;
-
-            // Fast path, try copying to the available memory directly
-            var advanceBy = 0;
-            fixed (byte* output = &MemoryMarshal.GetReference(span))
-            {
-                var start = output;
-                if (number < 10 && bytesLeftInBlock >= 1)
-                {
-                    start[0] = (byte)(number + AsciiDigitStart);
-                    advanceBy = 1;
-                }
-                else if (number < 100 && bytesLeftInBlock >= 2)
-                {
-                    var tens = (byte)((number * 205u) >> 11); // div10, valid to 1028
-
-                    start[0] = (byte)(tens + AsciiDigitStart);
-                    start[1] = (byte)(number - (tens * 10) + AsciiDigitStart);
-                    advanceBy = 2;
-                }
-                else if (number < 1000 && bytesLeftInBlock >= 3)
-                {
-                    var digit0 = (byte)((number * 41u) >> 12); // div100, valid to 1098
-                    var digits01 = (byte)((number * 205u) >> 11); // div10, valid to 1028
-
-                    start[0] = (byte)(digit0 + AsciiDigitStart);
-                    start[1] = (byte)(digits01 - (digit0 * 10) + AsciiDigitStart);
-                    start[2] = (byte)(number - (digits01 * 10) + AsciiDigitStart);
-                    advanceBy = 3;
-                }
-            }
-
-            if (advanceBy > 0)
-            {
-                buffer.Advance(advanceBy);
-            }
-            else
-            {
-                WriteNumericMultiWrite(ref buffer, number);
-            }
-        }
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static void WriteNumericMultiWrite<T>(ref this BufferWriter<T> buffer, uint number)
-             where T : struct, IBufferWriter<byte>
-        {
-            const byte AsciiDigitStart = (byte)'0';
-
-            var value = number;
-            var position = _maxULongByteLength;
-            var byteBuffer = NumericBytesScratch;
-            do
-            {
-                // Consider using Math.DivRem() if available
-                var quotient = value / 10;
-                byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
-                value = quotient;
-            }
-            while (value != 0);
-
-            var length = _maxULongByteLength - position;
-            buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
-        }
-
-        private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static byte[] CreateNumericBytesScratch()
-        {
-            var bytes = new byte[_maxULongByteLength];
-            _numericBytesScratch = bytes;
-            return bytes;
-        }
-    }
-}

+ 0 - 332
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsText.cs

@@ -1,332 +0,0 @@
-// 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.Buffers;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace PlatformBenchmarks
-{
-    internal static class BufferExtensionsText
-    {
-        private readonly static AsciiString _htmlLessThan = "&lt;";
-        private readonly static AsciiString _htmlGreaterThan = "&gt;";
-        private readonly static AsciiString _htmlAmpersand = "&amp;";
-        private readonly static AsciiString _htmlDoubleQuote = "&quot;";
-        private readonly static AsciiString _htmlSingleQuote = "&apos;";
-        private readonly static AsciiString _htmlEscapeStart = "&#";
-        private readonly static AsciiString _htmlEscapeEnd = ";";
-
-        public unsafe static void WriteUtf8String<T>(ref this BufferWriter<T> buffer, string text)
-             where T : struct, IBufferWriter<byte>
-        {
-            if (string.IsNullOrEmpty(text)) return;
-
-            fixed (char* start = text)
-            {
-                WriteUtf8(ref buffer, start, text.Length);
-            }
-        }
-
-        public unsafe static void WriteUtf8HtmlString<T>(ref this BufferWriter<T> buffer, string text)
-            where T : struct, IBufferWriter<byte>
-        {
-            if (string.IsNullOrEmpty(text)) return;
-
-            fixed (char* start = text)
-            {
-                var current = start;
-                var length = text.Length;
-
-                var firstIndexToEncode = FirstCharToHtmlEncode(current, length);
-                if (firstIndexToEncode == -1) 
-                {
-                    // Nothing to html encode.
-                    WriteUtf8(ref buffer, current, length);
-                    return;
-                }
-
-                while (true)
-                {
-                    if (firstIndexToEncode > 0)
-                    {
-                        WriteUtf8(ref buffer, current, firstIndexToEncode);
-                        current += firstIndexToEncode;
-                        length -= firstIndexToEncode;
-                    }
-
-                    var charsUsed = EncodeHtml(ref buffer, current, length);
-                    current += charsUsed;
-                    length -= charsUsed;
-
-                    if (length > 0)
-                    {
-                        firstIndexToEncode = FirstCharToHtmlEncode(current, length);
-                        if (firstIndexToEncode == -1)
-                        {
-                            // nothing left to html encode
-                            WriteUtf8(ref buffer, current, length);
-                            break;
-                        }
-
-                        continue;
-                    }
-
-                    break; // Done
-                }
-            }
-        }
-
-        private unsafe static void WriteUtf8<T>(ref this BufferWriter<T> buffer, char* input, int length)
-             where T : struct, IBufferWriter<byte>
-        {
-            var current = input;
-            var output = buffer.Span;
-            while (true)
-            {
-                var lengthToEncode = Math.Min(length, output.Length);
-                fixed (byte* pOutput = &MemoryMarshal.GetReference(output))
-                {
-                    var success = TryEncodeAsciiCharsToBytes(current, pOutput, lengthToEncode, out var consumed);
-                    length -= consumed;
-                    current += consumed;
-                    buffer.Advance(consumed);
-                    if (!success)
-                    {
-                        // Non-Ascii, move to Utf8 encoding
-                        WriteUtf8Encoding(ref buffer, current, length);
-                        return;
-                    }
-                }
-
-                if (length > 0)
-                {
-                    // Need more output space, get a new span
-                    buffer.Ensure(output.Length + 1);
-                    output = buffer.Span;
-                    continue;
-                }
-
-                break; // Done
-            }
-        }
-
-        private static unsafe void WriteUtf8Encoding<T>(ref this BufferWriter<T> buffer, char* input, int length)
-             where T : struct, IBufferWriter<byte>
-        {
-            var current = input;
-            var output = buffer.Span;
-            while (true)
-            {
-                var charsToUse = length;
-                while (charsToUse > 0)
-                {
-                    if (Encoding.UTF8.GetByteCount(current, length) <= output.Length)
-                    {
-                        int byteCount;
-                        fixed (byte* pBuffer = &MemoryMarshal.GetReference(output))
-                        {
-                            byteCount = Encoding.UTF8.GetBytes(current, charsToUse, pBuffer, output.Length);
-                        }
-
-                        buffer.Advance(byteCount);
-                        length -= charsToUse;
-                        current += charsToUse;
-                        break;
-                    }
-
-                    // Try again with 1/2 the count
-                    charsToUse /= 2;
-                }
-
-                if (length > 0)
-                {
-                    // Get a new span
-                    buffer.Ensure(output.Length + 1);
-                    output = buffer.Span;
-                    continue;
-                }
-
-                break; // Done
-            }
-        }
-
-        private unsafe static int EncodeHtml<T>(ref this BufferWriter<T> buffer, char* input, int length)
-            where T : struct, IBufferWriter<byte>
-        {
-            var encoded = 0;
-            while (true)
-            {
-                var ch = input[0];
-                switch (ch)
-                {
-                    case '\"':
-                        buffer.Write(_htmlDoubleQuote);
-                        break;
-                    case '&':
-                        buffer.Write(_htmlAmpersand);
-                        break;
-                    case '\'':
-                        buffer.Write(_htmlSingleQuote);
-                        break;
-                    case '<':
-                        buffer.Write(_htmlLessThan);
-                        break;
-                    case '>':
-                        buffer.Write(_htmlGreaterThan);
-                        break;
-                    default:
-                        buffer.Write(_htmlEscapeStart);
-                        buffer.WriteNumeric(ch);
-                        buffer.Write(_htmlEscapeEnd);
-                        break;
-                }
-
-                encoded++;
-                length--;
-
-                if (length > 0 && IsCharacterHtmlEncoded(input[1]))
-                {
-                    input++;
-                    // Next char also should be encoded
-                    continue;
-                }
-
-                break; // Done
-            }
-
-            return encoded;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static bool IsCharacterHtmlEncoded(char character)
-        {
-            const int htmlEncodedChars = 
-                (0x1 << ('<' & 0x1F)) | 
-                (0x1 << ('>' & 0x1F)) | 
-                (0x1 << ('&' & 0x1F)) | 
-                (0x1 << ('\'' & 0x1F)) | 
-                (0x1 << ('\"' & 0x1F));
-
-            bool result;
-            if (character >= 'A')
-            {
-                result = false;
-            }
-            else if (character >= ' ')
-            {
-                result = ((htmlEncodedChars >> (character & 0x1F)) & 0x1) == 0 ? false : true;
-            }
-            else
-            {
-                // Control chars, encode these as &#1;
-                result = true;
-            }
-
-            return result;
-        }
-
-        private static unsafe int FirstCharToHtmlEncode(char* text, int textLength)
-        {
-            var i = 0;
-            for (; i < textLength; i++)
-            {
-                if (IsCharacterHtmlEncoded(text[i]))
-                {
-                    break;
-                }
-            }
-
-            return i < textLength ? i : -1;
-        }
-
-        // Encode as bytes upto the first non-ASCII byte and return count encoded
-        private static unsafe bool TryEncodeAsciiCharsToBytes(char* input, byte* output, int length, out int consumed)
-        {
-            // Note: Not BIGENDIAN
-            const int Shift16Shift24 = (1 << 16) | (1 << 24);
-            const int Shift8Identity = (1 << 8) | (1);
-
-            var i = 0;
-
-            if (length < 4) goto trailing;
-
-            var unaligned = (int)(((ulong)input) & 0x7) >> 1;
-            // Unaligned chars
-            for (; i < unaligned; i++)
-            {
-                var ch = input[i];
-                if (ch > 0x7f)
-                {
-                    goto exit; // Non-ascii
-                }
-                output[i] = (byte)ch; // Cast convert
-            }
-
-            // Aligned
-            int ulongDoubleCount = (length - i) & ~0x7;
-            for (; i < ulongDoubleCount; i += 8)
-            {
-                ulong inputUlong0 = *(ulong*)(input + i);
-                ulong inputUlong1 = *(ulong*)(input + i + 4);
-                // Pack 16 ASCII chars into 16 bytes
-                if ((inputUlong0 & 0xFF80FF80FF80FF80u) == 0)
-                {
-                    *(uint*)(output + i) =
-                        ((uint)((inputUlong0 * Shift16Shift24) >> 24) & 0xffff) |
-                        ((uint)((inputUlong0 * Shift8Identity) >> 24) & 0xffff0000);
-                }
-                else
-                {
-                    goto exit; // Non-ascii
-                }
-                if ((inputUlong1 & 0xFF80FF80FF80FF80u) == 0)
-                {
-                    *(uint*)(output + i + 4) =
-                        ((uint)((inputUlong1 * Shift16Shift24) >> 24) & 0xffff) |
-                        ((uint)((inputUlong1 * Shift8Identity) >> 24) & 0xffff0000);
-                }
-                else
-                {
-                    i += 4;
-                    goto exit; // Non-ascii
-                }
-            }
-            if (length - 4 > i)
-            {
-                ulong inputUlong = *(ulong*)(input + i);
-                if ((inputUlong & 0xFF80FF80FF80FF80u) == 0)
-                {
-                    // Pack 8 ASCII chars into 8 bytes
-                    *(uint*)(output + i) =
-                        ((uint)((inputUlong * Shift16Shift24) >> 24) & 0xffff) |
-                        ((uint)((inputUlong * Shift8Identity) >> 24) & 0xffff0000);
-                }
-                else
-                {
-                    goto exit; // Non-ascii
-                }
-
-                i += 4;
-            }
-
-        trailing:
-            for (; i < length; i++)
-            {
-                var ch = input[i];
-                if (ch > 0x7f)
-                {
-                    goto exit; // Hit non-ascii
-                }
-
-                output[i] = (byte)ch; // Cast convert
-            }
-
-        exit:
-            consumed = i;
-            return length == i ? true : false;
-        }
-    }
-}

+ 3 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/appsettings.json

@@ -0,0 +1,3 @@
+{
+  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnetcore-Benchmarks;Trusted_Connection=True;MultipleActiveResultSets=true"
+}

+ 4 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/appsettings.postgresql.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=32;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Delay Us=500;Write Coalescing Buffer Threshold Bytes=5500",
+  "Database": "postgresql"
+}

+ 0 - 12
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-ef-pg.dockerfile

@@ -1,12 +0,0 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
-WORKDIR /app
-COPY Benchmarks .
-RUN dotnet publish -c Release -o out
-
-FROM mono:latest AS runtime
-ENV ASPNETCORE_URLS http://+:8080
-WORKDIR /app
-COPY --from=build /app/out ./
-COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
-
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=MvcDbSingleQueryEf,MvcDbMultiQueryEf,MvcDbMultiUpdateEf,MvcDbFortunesEf"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-my.dockerfile

@@ -1,12 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=MvcDbSingleQueryRaw,MvcDbMultiQueryRaw,MvcDbMultiUpdateRaw,MvcDbFortunesRaw"]
+ENTRYPOINT ["./Benchmarks", "scenarios=MvcDbSingleQueryRaw,MvcDbMultiQueryRaw,MvcDbMultiUpdateRaw,MvcDbFortunesRaw"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-pg.dockerfile

@@ -1,12 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
 COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=MvcDbSingleQueryRaw,MvcDbMultiQueryRaw,MvcDbMultiUpdateRaw,MvcDbFortunesRaw"]
+ENTRYPOINT ["./Benchmarks", "scenarios=MvcDbSingleQueryRaw,MvcDbMultiQueryRaw,MvcDbFortunesRaw,MvcDbMultiUpdateRaw"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc.dockerfile

@@ -1,11 +1,11 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=mvcplaintext,mvcjson"]
+ENTRYPOINT ["./Benchmarks", "scenarios=mvcplaintext,mvcjson"]

+ 0 - 12
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-ef-pg.dockerfile

@@ -1,12 +0,0 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
-WORKDIR /app
-COPY Benchmarks .
-RUN dotnet publish -c Release -o out
-
-FROM mono:latest AS runtime
-ENV ASPNETCORE_URLS http://+:8080
-WORKDIR /app
-COPY --from=build /app/out ./
-COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
-
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=DbSingleQueryEf,DbMultiQueryEf,DbMultiUpdateEf,DbFortunesEf"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-my.dockerfile

@@ -1,12 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=DbSingleQueryRaw,DbMultiQueryRaw,DbMultiUpdateRaw,DbFortunesRaw"]
+ENTRYPOINT ["./Benchmarks", "scenarios=DbSingleQueryRaw,DbMultiQueryRaw,DbMultiUpdateRaw,DbFortunesRaw"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-pg.dockerfile

@@ -1,12 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
 COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=DbSingleQueryRaw,DbMultiQueryRaw,DbMultiUpdateRaw,DbFortunesRaw"]
+ENTRYPOINT ["./Benchmarks", "scenarios=DbSingleQueryRaw,DbMultiQueryRaw,DbFortunesRaw,DbMultiUpdateRaw"]

+ 3 - 3
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw.dockerfile

@@ -1,11 +1,11 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY Benchmarks .
 COPY Benchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "Benchmarks.exe", "scenarios=plaintext,json"]
+ENTRYPOINT ["./Benchmarks", "scenarios=plaintext,json"]

+ 4 - 5
frameworks/CSharp/aspnetcore-mono/aspcore-mono-my.dockerfile

@@ -1,13 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY PlatformBenchmarks .
 COPY PlatformBenchmarks .
-RUN dotnet publish -c Release -o out
+RUN dotnet publish -c Release -o out /p:IsDatabase=true
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
-ENV KestrelTransport Libuv
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 COPY Benchmarks/appsettings.mysql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "PlatformBenchmarks.exe"]
+ENTRYPOINT ["./PlatformBenchmarks"]

+ 5 - 6
frameworks/CSharp/aspnetcore-mono/aspcore-mono-pg.dockerfile

@@ -1,13 +1,12 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY PlatformBenchmarks .
 COPY PlatformBenchmarks .
-RUN dotnet publish -c Release -o out
+RUN dotnet publish -c Release -o out /p:IsDatabase=true
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
-ENV KestrelTransport Libuv
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
-COPY Benchmarks/appsettings.postgresql.json ./appsettings.json
+COPY PlatformBenchmarks/appsettings.postgresql.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "PlatformBenchmarks.exe"]
+ENTRYPOINT ["./PlatformBenchmarks"]

+ 4 - 4
frameworks/CSharp/aspnetcore-mono/aspcore-mono.dockerfile

@@ -1,13 +1,13 @@
-FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
 WORKDIR /app
 WORKDIR /app
 COPY PlatformBenchmarks .
 COPY PlatformBenchmarks .
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
-FROM mono:latest AS runtime
+FROM mcr.microsoft.com/dotnet/runtime-deps:5.0 AS runtime
 ENV ASPNETCORE_URLS http://+:8080
 ENV ASPNETCORE_URLS http://+:8080
-ENV KestrelTransport Libuv
+ENV DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS 1
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 COPY Benchmarks/appsettings.json ./appsettings.json
 COPY Benchmarks/appsettings.json ./appsettings.json
 
 
-ENTRYPOINT ["mono", "--server", "--gc=sgen", "--gc-params=mode=throughput", "PlatformBenchmarks.exe"]
+ENTRYPOINT ["./PlatformBenchmarks"]

+ 167 - 229
frameworks/CSharp/aspnetcore-mono/benchmark_config.json

@@ -1,230 +1,168 @@
 {
 {
-  "framework": "aspcore-mono",
-  "tests": [{
-    "default": {
-      "plaintext_url": "/plaintext",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono",
-      "notes": "",
-      "versus": "aspcore"
-    },
-    "pg": {
-      "fortune_url": "/fortunes",
-      "db_url": "/db",
-      "query_url": "/queries/queries=",
-      "update_url": "/updates/queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "Postgres",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono",
-      "notes": "",
-      "versus": "aspcore-ado-pg"
-    },
-    "my": {
-      "fortune_url": "/fortunes",
-      "db_url": "/db",
-      "query_url": "/queries/queries=",
-      "update_url": "/updates/queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "MySQL",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono",
-      "notes": "",
-      "versus": "aspcore-ado-my"
-    },
-    "mw": {
-      "plaintext_url": "/plaintext",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "None",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, Middleware",
-      "notes": "",
-      "versus": "aspcore-mono"
-    },
-    "mw-pg": {
-      "db_url": "/db/raw",
-      "query_url": "/queries/raw?queries=",
-      "update_url": "/updates/raw?queries=",
-      "fortune_url": "/fortunes/raw",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "Postgres",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, Middleware",
-      "notes": "",
-      "versus": "aspcore-mono-pg"
-    },
-    "mw-my": {
-      "db_url": "/db/raw",
-      "query_url": "/queries/raw?queries=",
-      "update_url": "/updates/raw?queries=",
-      "fortune_url": "/fortunes/raw",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, Middleware",
-      "notes": "",
-      "versus": "aspcore-mono-my"
-    },
-    "mw-ef-pg": {
-      "db_url": "/db/ef",
-      "query_url": "/queries/ef?queries=",
-      "update_url": "/updates/ef?queries=",
-      "fortune_url": "/fortunes/ef",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "Postgres",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Full",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, Middleware",
-      "notes": "",
-      "versus": "aspcore-mono-pg"
-    },
-    "mvc": {
-      "plaintext_url": "/mvc/plaintext",
-      "json_url": "/mvc/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Fullstack",
-      "database": "None",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, MVC",
-      "notes": "",
-      "versus": "aspcore-mono"
-    },
-    "mvc-pg": {
-      "db_url": "/mvc/db/raw",
-      "query_url": "/mvc/queries/raw?queries=",
-      "update_url": "/mvc/updates/raw?queries=",
-      "fortune_url": "/mvc/fortunes/raw",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Fullstack",
-      "database": "Postgres",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, MVC",
-      "notes": "",
-      "versus": "aspcore-mono-pg"
-    },
-    "mvc-ef-pg": {
-      "db_url": "/mvc/db/ef",
-      "query_url": "/mvc/queries/ef?queries=",
-      "update_url": "/mvc/updates/ef?queries=",
-      "fortune_url": "/mvc/fortunes/ef",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Fullstack",
-      "database": "Postgres",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Full",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, MVC",
-      "notes": "",
-      "versus": "aspcore-mono-pg"
-    },
-    "mvc-my": {
-      "db_url": "/mvc/db/raw",
-      "query_url": "/mvc/queries/raw?queries=",
-      "update_url": "/mvc/updates/raw?queries=",
-      "fortune_url": "/mvc/fortunes/raw",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Fullstack",
-      "database": "MySQL",
-      "framework": "ASP.NET Core",
-      "language": "C#",
-      "orm": "Raw",
-      "platform": ".NET",
-      "flavor": "Mono",
-      "webserver": "Kestrel",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ASP.NET Core on Mono, MVC",
-      "notes": "",
-      "versus": "aspcore-mono-my"
-    }
-  }]
-}
+	"framework": "aspcore-mono",
+	"tests": [{
+		"default": {
+			"plaintext_url": "/plaintext",
+			"json_url": "/json",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Platform",
+			"database": "None",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono",
+			"notes": "",
+			"versus": "aspcore"
+		},
+		"pg": {
+			"fortune_url": "/fortunes",
+			"db_url": "/db",
+			"query_url": "/queries/",
+			"update_url": "/updates/",
+			"cached_query_url": "/cached-worlds/",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Platform",
+			"database": "Postgres",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono",
+			"notes": "",
+			"versus": "aspcore-ado-pg"
+		},
+		"mw": {
+			"plaintext_url": "/plaintext",
+			"json_url": "/json",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Micro",
+			"database": "None",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, Middleware",
+			"notes": "",
+			"versus": "aspcore-mono"
+		},
+		"mw-pg": {
+			"db_url": "/db/raw",
+			"query_url": "/queries/raw?queries=",
+			"update_url": "/updates/raw?queries=",
+			"fortune_url": "/fortunes/raw",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Micro",
+			"database": "Postgres",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, Middleware",
+			"notes": "",
+			"versus": "aspcore-mono-pg"
+		},
+		"mw-my": {
+			"db_url": "/db/raw",
+			"query_url": "/queries/raw?queries=",
+			"update_url": "/updates/raw?queries=",
+			"fortune_url": "/fortunes/raw",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Micro",
+			"database": "MySQL",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, Middleware",
+			"notes": "",
+			"versus": "aspcore-mono-my"
+		},
+		"mvc": {
+			"plaintext_url": "/mvc/plaintext",
+			"json_url": "/mvc/json",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Fullstack",
+			"database": "None",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, MVC",
+			"notes": "",
+			"versus": "aspcore-mono"
+		},
+		"mvc-pg": {
+			"db_url": "/mvc/db/raw",
+			"query_url": "/mvc/queries/raw?queries=",
+			"update_url": "/mvc/updates/raw?queries=",
+			"fortune_url": "/mvc/fortunes/raw",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Fullstack",
+			"database": "Postgres",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, MVC",
+			"notes": "",
+			"versus": "aspcore-mono-pg"
+		},
+		"mvc-my": {
+			"db_url": "/mvc/db/raw",
+			"query_url": "/mvc/queries/raw?queries=",
+			"update_url": "/mvc/updates/raw?queries=",
+			"fortune_url": "/mvc/fortunes/raw",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Fullstack",
+			"database": "MySQL",
+			"framework": "ASP.NET Core",
+			"language": "C#",
+			"orm": "Raw",
+			"platform": ".NET",
+			"flavor": "Mono",
+			"webserver": "Kestrel",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "ASP.NET Core on Mono, MVC",
+			"notes": "",
+			"versus": "aspcore-mono-my"
+		}
+	}]
+}