Преглед изворни кода

Add aspnetcore-mono (ASP.NET Core on Mono) (#3764)

* Add aspnetcore-mono (ASP.NET Core on Mono)

* Fix Disconnect issue
Ben Adams пре 7 година
родитељ
комит
72b51227f3
83 измењених фајлова са 4023 додато и 0 уклоњено
  1. 38 0
      frameworks/CSharp/aspnetcore-mono/.gitignore
  2. 48 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.csproj
  3. 22 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.sln
  4. 12 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/AppSettings.cs
  5. 15 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/ConsoleArgs.cs
  6. 85 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/ConsoleHostScenariosConfiguration.cs
  7. 14 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/DatabaseServer.cs
  8. 20 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/EnabledScenario.cs
  9. 10 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/IScenariosConfiguration.cs
  10. 167 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/Scenarios.cs
  11. 28 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/FortunesController.cs
  12. 46 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/HomeController.cs
  13. 35 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs
  14. 35 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs
  15. 34 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs
  16. 21 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/ApplicationDbContext.cs
  17. 24 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/BatchUpdateString.cs
  18. 97 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs
  19. 31 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Fortune.cs
  20. 19 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IDb.cs
  21. 10 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/IRandom.cs
  22. 20 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Random.cs
  23. 172 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/RawDb.cs
  24. 17 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.cs
  25. 51 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesEfMiddleware.cs
  26. 51 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesRawMiddleware.cs
  27. 56 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/JsonMiddleware.cs
  28. 56 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MiddlewareHelpers.cs
  29. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs
  30. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesRawMiddleware.cs
  31. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs
  32. 62 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs
  33. 52 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/PlaintextMiddleware.cs
  34. 59 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryEfMiddleware.cs
  35. 60 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryRawMiddleware.cs
  36. 8 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/NuGet.Config
  37. 75 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Program.cs
  38. 184 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs
  39. 12 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Fortunes/Fortunes.cshtml
  40. 5 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Home/Index.cshtml
  41. 3 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/appsettings.json
  42. 4 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/appsettings.mysql.json
  43. 4 0
      frameworks/CSharp/aspnetcore-mono/Benchmarks/appsettings.postgresql.json
  44. BIN
      frameworks/CSharp/aspnetcore-mono/Benchmarks/wwwroot/1KB.txt
  45. 50 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/AsciiString.cs
  46. 63 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
  47. 181 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
  48. 45 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Json.cs
  49. 44 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.PlainText.cs
  50. 122 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs
  51. 81 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs
  52. 12 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Configuration/AppSettings.cs
  53. 13 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Configuration/DatabaseServer.cs
  54. 24 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs
  55. 25 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Fortune.cs
  56. 19 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IDb.cs
  57. 10 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/IRandom.cs
  58. 20 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Random.cs
  59. 171 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs
  60. 12 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/World.cs
  61. 29 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/HttpApplication.cs
  62. 17 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/IHttpConnection.cs
  63. 8 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/NuGet.Config
  64. 38 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/PlatformBenchmarks.csproj
  65. 68 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs
  66. 16 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Startup.cs
  67. 106 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsNumeric.cs
  68. 332 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferExtensionsText.cs
  69. 95 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferWriter.cs
  70. 62 0
      frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/DateHeader.cs
  71. 55 0
      frameworks/CSharp/aspnetcore-mono/README.md
  72. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-ef-pg.dockerfile
  73. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-my.dockerfile
  74. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc-pg.dockerfile
  75. 11 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc.dockerfile
  76. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-ef-pg.dockerfile
  77. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-my.dockerfile
  78. 12 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw-pg.dockerfile
  79. 11 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw.dockerfile
  80. 13 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-my.dockerfile
  81. 13 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono-pg.dockerfile
  82. 13 0
      frameworks/CSharp/aspnetcore-mono/aspcore-mono.dockerfile
  83. 224 0
      frameworks/CSharp/aspnetcore-mono/benchmark_config.json

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

@@ -0,0 +1,38 @@
+[Oo]bj/
+[Bb]in/
+[Oo]ut/
+TestResults/
+.nuget/
+*.sln.ide/
+_ReSharper.*/
+.idea/
+packages/
+artifacts/
+PublishProfiles/
+.vs/
+*.user
+*.suo
+*.cache
+*.docstates
+_ReSharper.*
+nuget.exe
+*net45.csproj
+*net451.csproj
+*k10.csproj
+*.psess
+*.vsp
+*.pidb
+*.userprefs
+*DS_Store
+*.ncrunchsolution
+*.*sdf
+*.ipch
+*.swp
+*~
+.build/
+.testPublish/
+launchSettings.json
+BenchmarkDotNet.Artifacts/
+BDN.Generated/
+binaries/
+global.json

+ 48 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.csproj

@@ -0,0 +1,48 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+  <PropertyGroup>
+    <TargetFramework>net471</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <LangVersion>7.3</LangVersion>
+    <RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
+    <!-- Add the myget feed, without altering any other configurations. -->
+    <RestoreAdditionalProjectSources>https://dotnet.myget.org/F/dotnet-core/api/v3/index.json</RestoreAdditionalProjectSources>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Update="wwwroot/**" CopyToOutputDirectory="PreserveNewest" />
+    <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
+    <None Include="appsettings.postgresql.json" CopyToOutputDirectory="PreserveNewest" />
+    <None Include="appsettings.mysql.json" CopyToOutputDirectory="PreserveNewest" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.0-rc1-final" />
+    <!-- Fix https://github.com/aspnet/KestrelHttpServer/pull/2562  -->
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.1.0-rtm-30793" />
+    <PackageReference Include="MySqlConnector" Version="0.40.3" />
+    <PackageReference Include="Npgsql" Version="4.0.0-rc1" />
+    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.0-rc1" />
+  </ItemGroup>
+
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net471' ">
+    <!-- Make .NET 4.7.1 reference assemblies available even on Linux -->
+    <PackageReference Include="Microsoft.TargetingPack.NETFramework.v4.7.1" Version="1.0.0" ExcludeAssets="All" PrivateAssets="All" />
+    <Reference Include="netstandard" />
+    <Reference Include="System.Runtime" />
+    <Reference Include="System.Threading.Tasks" />
+    <PackageReference Include="NETStandard.Library" Version="2.0.0" />
+  </ItemGroup>
+
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net471' ">
+    <!-- Make sure the reference assemblies are available -->
+    <FrameworkPathOverride>$(NuGetPackageRoot)microsoft.targetingpack.netframework.v4.7.1\1.0.0\lib\net471\</FrameworkPathOverride>
+  </PropertyGroup>
+</Project>

+ 22 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Benchmarks.sln

@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26507.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Benchmarks.csproj", "{8D5521DB-5F71-4F26-9E60-92A158E1F50E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8D5521DB-5F71-4F26-9E60-92A158E1F50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8D5521DB-5F71-4F26-9E60-92A158E1F50E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8D5521DB-5F71-4F26-9E60-92A158E1F50E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8D5521DB-5F71-4F26-9E60-92A158E1F50E}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 12 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/AppSettings.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Benchmarks.Configuration
+{
+    public class AppSettings
+    {
+        public string ConnectionString { get; set; }
+
+        public DatabaseServer Database { get; set; } = DatabaseServer.None;
+    }
+}

+ 15 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/ConsoleArgs.cs

@@ -0,0 +1,15 @@
+// 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.Configuration
+{
+    public class ConsoleArgs
+    {
+        public ConsoleArgs(string[] args)
+        {
+            Args = args;
+        }
+
+        public string[] Args { get; }
+    }
+}

+ 85 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/ConsoleHostScenariosConfiguration.cs

@@ -0,0 +1,85 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+
+namespace Benchmarks.Configuration
+{
+    public class ConsoleHostScenariosConfiguration : IScenariosConfiguration
+    {
+        private readonly string[] _args;
+
+        public ConsoleHostScenariosConfiguration(ConsoleArgs args)
+        {
+            _args = args.Args;
+        }
+
+        public void ConfigureScenarios(Scenarios scenarios)
+        {
+            var scenarioConfig = new ConfigurationBuilder()
+                .AddJsonFile("scenarios.json", optional: true)
+                .AddCommandLine(_args)
+                .Build();
+
+            var enabledCount = 0;
+            var configuredScenarios = scenarioConfig["scenarios"];
+            if (!string.IsNullOrWhiteSpace(configuredScenarios))
+            {
+                Console.WriteLine("Scenario configuration found in scenarios.json and/or command line args");
+                var choices = configuredScenarios.Split(',');
+                foreach (var choice in choices)
+                {
+                    enabledCount += scenarios.Enable(choice);
+                }
+            }
+            else
+            {
+                Console.WriteLine("Which scenarios would you like to enable?:");
+                Console.WriteLine();
+                foreach (var scenario in Scenarios.GetNames())
+                {
+                    Console.WriteLine("  " + scenario);
+                }
+                Console.WriteLine();
+                Console.WriteLine("Type full or partial scenario names separated by commas and hit [Enter]");
+                Console.Write("> ");
+
+                var choices = Console.ReadLine().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+                if (choices.Length > 0)
+                {
+                    foreach (var choice in choices)
+                    {
+                        enabledCount += scenarios.Enable(choice);
+                    }
+                }
+            }
+
+            if (enabledCount == 0)
+            {
+                Console.WriteLine();
+                Console.WriteLine("No matching scenarios found, enabling defaults");
+                scenarios.EnableDefault();
+            }
+
+            PrintEnabledScenarios(scenarios.GetEnabled());
+        }
+
+        private static void PrintEnabledScenarios(IEnumerable<EnabledScenario> scenarios)
+        {
+            Console.WriteLine();
+            Console.WriteLine("The following scenarios were enabled:");
+
+            var maxNameLength = scenarios.Max(s => s.Name.Length);
+
+            foreach (var scenario in scenarios)
+            {
+                Console.WriteLine($"  {scenario.Name.PadRight(maxNameLength)} -> {string.Join($"{Environment.NewLine}{"".PadLeft(maxNameLength + 6)}", scenario.Paths)}");
+            }
+            Console.WriteLine();
+        }
+    }
+}

+ 14 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/DatabaseServer.cs

@@ -0,0 +1,14 @@
+// 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.Configuration
+{
+    public enum DatabaseServer
+    {
+        None,
+        SqlServer,
+        PostgreSql,
+        MySql
+    }
+}

+ 20 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/EnabledScenario.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+
+namespace Benchmarks.Configuration
+{
+    public class EnabledScenario
+    {
+        public EnabledScenario(string name, IEnumerable<string> paths)
+        {
+            Name = name;
+            Paths = paths;
+        }
+
+        public string Name { get; }
+
+        public IEnumerable<string> Paths { get; }
+    }
+}

+ 10 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Configuration/IScenariosConfiguration.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.Configuration
+{
+    public interface IScenariosConfiguration
+    {
+        void ConfigureScenarios(Scenarios scenarios);
+    }
+}

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

@@ -0,0 +1,167 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Internal;
+
+namespace Benchmarks.Configuration
+{
+    public class Scenarios
+    {
+        public Scenarios(IScenariosConfiguration scenariosConfiguration)
+        {
+            scenariosConfiguration.ConfigureScenarios(this);
+        }
+
+        [ScenarioPath("/plaintext")]
+        public bool Plaintext { get; set; }
+
+        [ScenarioPath("/json")]
+        public bool Json { get; set; }
+
+        [ScenarioPath("/128B.txt", "/512B.txt", "/1KB.txt", "/4KB.txt", "/16KB.txt", "/512KB.txt", "/1MB.txt", "/5MB.txt")]
+        public bool StaticFiles { get; set; }
+
+        [ScenarioPath("/db/raw")]
+        public bool DbSingleQueryRaw { get; set; }
+
+        [ScenarioPath("/db/ef")]
+        public bool DbSingleQueryEf { get; set; }
+
+        [ScenarioPath("/db/dapper")]
+        public bool DbSingleQueryDapper { get; set; }
+
+        [ScenarioPath("/queries/raw")]
+        public bool DbMultiQueryRaw { get; set; }
+
+        [ScenarioPath("/queries/ef")]
+        public bool DbMultiQueryEf { get; set; }
+
+        [ScenarioPath("/queries/dapper")]
+        public bool DbMultiQueryDapper { get; set; }
+
+        [ScenarioPath("/updates/raw")]
+        public bool DbMultiUpdateRaw { get; set; }
+
+        [ScenarioPath("/updates/ef")]
+        public bool DbMultiUpdateEf { get; set; }
+
+        [ScenarioPath("/updates/dapper")]
+        public bool DbMultiUpdateDapper { get; set; }
+
+        [ScenarioPath("/fortunes/raw")]
+        public bool DbFortunesRaw { get; set; }
+
+        [ScenarioPath("/fortunes/ef")]
+        public bool DbFortunesEf { get; set; }
+
+        [ScenarioPath("/fortunes/dapper")]
+        public bool DbFortunesDapper { get; set; }
+
+        [ScenarioPath("/mvc/plaintext")]
+        public bool MvcPlaintext { get; set; }
+
+        [ScenarioPath("/mvc/json")]
+        public bool MvcJson { get; set; }
+
+        [ScenarioPath("/mvc/view")]
+        public bool MvcViews { get; set; }
+
+        [ScenarioPath("/mvc/db/raw")]
+        public bool MvcDbSingleQueryRaw { get; set; }
+
+        [ScenarioPath("/mvc/db/dapper")]
+        public bool MvcDbSingleQueryDapper { get; set; }
+
+        [ScenarioPath("/mvc/db/ef")]
+        public bool MvcDbSingleQueryEf { get; set; }
+
+        [ScenarioPath("/mvc/queries/raw")]
+        public bool MvcDbMultiQueryRaw { get; set; }
+
+        [ScenarioPath("/mvc/queries/dapper")]
+        public bool MvcDbMultiQueryDapper { get; set; }
+
+        [ScenarioPath("/mvc/queries/ef")]
+        public bool MvcDbMultiQueryEf { get; set; }
+
+        [ScenarioPath("/mvc/updates/raw")]
+        public bool MvcDbMultiUpdateRaw { get; set; }
+
+        [ScenarioPath("/mvc/updates/dapper")]
+        public bool MvcDbMultiUpdateDapper { get; set; }
+
+        [ScenarioPath("/mvc/updates/ef")]
+        public bool MvcDbMultiUpdateEf { get; set; }
+
+        [ScenarioPath("/mvc/fortunes/raw")]
+        public bool MvcDbFortunesRaw { get; set; }
+
+        [ScenarioPath("/mvc/fortunes/ef")]
+        public bool MvcDbFortunesEf { get; set; }
+
+        [ScenarioPath("/mvc/fortunes/dapper")]
+        public bool MvcDbFortunesDapper { get; set; }
+
+        public bool Any(string partialName) =>
+            typeof(Scenarios).GetTypeInfo().DeclaredProperties
+                .Where(p => p.Name.IndexOf(partialName, StringComparison.Ordinal) >= 0 && (bool)p.GetValue(this))
+                .Any();
+
+        public IEnumerable<EnabledScenario> GetEnabled() =>
+            typeof(Scenarios).GetTypeInfo().DeclaredProperties
+                .Where(p => p.GetValue(this) is bool && (bool)p.GetValue(this))
+                .Select(p => new EnabledScenario(p.Name, p.GetCustomAttribute<ScenarioPathAttribute>()?.Paths));
+
+        public static IEnumerable<string> GetNames() =>
+            typeof(Scenarios).GetTypeInfo().DeclaredProperties
+                .Select(p => p.Name);
+
+        public static string[] GetPaths(Expression<Func<Scenarios, bool>> scenarioExpression) =>
+            scenarioExpression.GetPropertyAccess().GetCustomAttribute<ScenarioPathAttribute>().Paths;
+
+        public static string GetPath(Expression<Func<Scenarios, bool>> scenarioExpression) =>
+            GetPaths(scenarioExpression)[0];
+
+        public int Enable(string partialName)
+        {
+            if (string.Equals(partialName, "[default]", StringComparison.OrdinalIgnoreCase))
+            {
+                EnableDefault();
+                return 2;
+            }
+
+            var props = typeof(Scenarios).GetTypeInfo().DeclaredProperties
+                .Where(p => string.Equals(partialName, "[all]", StringComparison.OrdinalIgnoreCase) || p.Name.StartsWith(partialName, StringComparison.OrdinalIgnoreCase))
+                .ToList();
+
+            foreach (var p in props)
+            {
+                p.SetValue(this, true);
+            }
+
+            return props.Count;
+        }
+
+        public void EnableDefault()
+        {
+            Plaintext = true;
+            Json = true;
+        }
+    }
+
+    [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
+    public sealed class ScenarioPathAttribute : Attribute
+    {
+        public ScenarioPathAttribute(params string[] paths)
+        {
+            Paths = paths;
+        }
+
+        public string[] Paths { get; }
+    }
+}

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

@@ -0,0 +1,28 @@
+// 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.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/fortunes")]
+    public class FortunesController : Controller
+    {
+        [HttpGet("raw")]
+        public async Task<IActionResult> Raw()
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
+        }
+
+        [HttpGet("ef")]
+        public async Task<IActionResult> Ef()
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
+        }
+    }
+}

+ 46 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/HomeController.cs

@@ -0,0 +1,46 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc")]
+    public class HomeController : Controller
+    {
+        [HttpGet("plaintext")]
+        public IActionResult Plaintext()
+        {
+            return new PlainTextActionResult();
+        }
+
+        [HttpGet("json")]
+        [Produces("application/json")]
+        public object Json()
+        {
+            return new { message = "Hello, World!" };
+        }
+        
+        [HttpGet("view")]
+        public ViewResult Index()
+        {
+            return View();
+        }
+
+        private class PlainTextActionResult : IActionResult
+        {
+            private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
+
+            public Task ExecuteResultAsync(ActionContext context)
+            {
+                context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
+                context.HttpContext.Response.ContentType = "text/plain";
+                context.HttpContext.Response.ContentLength = _helloWorldPayload.Length;
+                return context.HttpContext.Response.Body.WriteAsync(_helloWorldPayload, 0, _helloWorldPayload.Length);
+            }
+        }
+    }
+}

+ 35 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleQueriesController.cs

@@ -0,0 +1,35 @@
+// 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.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/queries")]
+    public class MultipleQueriesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        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;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleQueriesRows(queries);
+        }
+    }
+}

+ 35 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/MultipleUpdatesController.cs

@@ -0,0 +1,35 @@
+// 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.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/updates")]
+    public class MultipleUpdatesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        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;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleUpdatesRows(queries);
+        }
+    }
+}

+ 34 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Controllers/SingleQueryController.cs

@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/db")]
+    public class SingleQueryController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World> Raw()
+        {
+            return ExecuteQuery<RawDb>();
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World> Ef()
+        {
+            return ExecuteQuery<EfDb>();
+        }
+
+        private Task<World> ExecuteQuery<T>() where T : IDb
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadSingleQueryRow();
+        }
+    }
+}

+ 21 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/ApplicationDbContext.cs

@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.EntityFrameworkCore;
+
+namespace Benchmarks.Data
+{
+    public sealed class ApplicationDbContext : DbContext
+    {
+        public ApplicationDbContext(DbContextOptions options)
+            : base(options)
+        {
+            ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
+            ChangeTracker.AutoDetectChangesEnabled = false;
+        }
+
+        public DbSet<World> World { get; set; }
+
+        public DbSet<Fortune> Fortune { get; set; }
+    }
+}

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

@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Benchmarks.Data
+{
+    internal class BatchUpdateString
+    {
+        public static IList<BatchUpdateString> Strings { get;} = 
+            Enumerable.Range(0, 500)
+                      .Select(i => new BatchUpdateString
+                      {
+                          Id = $"Id_{i}",
+                          Random = $"Random_{i}",
+                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                      }).ToArray();
+                        
+        public string Id { get; set; }
+        public string Random { get; set; }
+        public string UpdateQuery { get; set; }
+    }
+}

+ 97 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/EfDb.cs

@@ -0,0 +1,97 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.Extensions.Options;
+
+namespace Benchmarks.Data
+{
+    public class EfDb : IDb
+    {
+        private readonly IRandom _random;
+        private readonly ApplicationDbContext _dbContext;
+        private readonly bool _useBatchUpdate;
+
+        public EfDb(IRandom random, ApplicationDbContext dbContext, IOptions<AppSettings> appSettings)
+        {
+            _random = random;
+            _dbContext = dbContext;
+            _useBatchUpdate = appSettings.Value.Database != DatabaseServer.PostgreSql;
+        }
+
+        private static readonly Func<ApplicationDbContext, int, Task<World>> _firstWorldQuery
+            = EF.CompileAsyncQuery((ApplicationDbContext context, int id)
+                => context.World.First(w => w.Id == id));
+
+        public Task<World> LoadSingleQueryRow()
+        {
+            var id = _random.Next(1, 10001);
+
+            return _firstWorldQuery(_dbContext, id);
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            for (var i = 0; i < count; i++)
+            {
+                var id = _random.Next(1, 10001);
+
+                result[i] = await _firstWorldQuery(_dbContext, id);
+            }
+
+            return result;
+        }
+
+        private static readonly Func<ApplicationDbContext, int, Task<World>> _firstWorldTrackedQuery
+            = EF.CompileAsyncQuery((ApplicationDbContext context, int id)
+                => context.World.AsTracking().First(w => w.Id == id));
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            for (var i = 0; i < count; i++)
+            {
+                var id = _random.Next(1, 10001);
+                var result = await _firstWorldTrackedQuery(_dbContext, id);
+
+                _dbContext.Entry(result).Property("RandomNumber").CurrentValue = _random.Next(1, 10001);
+
+                results[i] = result;
+
+                if (!_useBatchUpdate)
+                {
+                    await _dbContext.SaveChangesAsync();
+                }
+            }
+
+            if (_useBatchUpdate)
+            {
+                await _dbContext.SaveChangesAsync();
+            }
+
+            return results;
+        }
+
+        private static readonly Func<ApplicationDbContext, AsyncEnumerable<Fortune>> _fortunesQuery
+            = EF.CompileAsyncQuery((ApplicationDbContext context) => context.Fortune);
+
+        public async Task<IEnumerable<Fortune>> LoadFortunesRows()
+        {
+            var result = await _fortunesQuery(_dbContext).ToListAsync();
+
+            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Sort();
+
+            return result;
+        }
+    }
+}

+ 31 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/Fortune.cs

@@ -0,0 +1,31 @@
+// 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.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Benchmarks.Data
+{
+    [Table("fortune")]
+    public class Fortune : IComparable<Fortune>, IComparable
+    {
+        [Column("id")]
+        public int Id { get; set; }
+
+        [Column("message")]
+        [StringLength(2048)]
+        public string Message { get; set; }
+        
+        public int CompareTo(object obj)
+        {
+            return CompareTo((Fortune)obj);
+        }
+
+        public int CompareTo(Fortune other)
+        {
+            // Performance critical, using culture insensitive comparison
+            return String.CompareOrdinal(Message, other.Message);
+        }
+    }
+}

+ 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<IEnumerable<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);
+    }
+}

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

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading;
+
+namespace Benchmarks.Data
+{
+    public class DefaultRandom : IRandom
+    {
+        private static int nextSeed = 0;
+        // Random isn't thread safe
+        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
+
+        public int Next(int minValue, int maxValue)
+        {
+            return _random.Value.Next(minValue, maxValue);
+        }
+    }
+}

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

@@ -0,0 +1,172 @@
+// 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;
+using System.Data.Common;
+using System.Text;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace Benchmarks.Data
+{
+    public class RawDb : IDb
+    {
+        private readonly IRandom _random;
+        private readonly DbProviderFactory _dbProviderFactory;
+        private readonly string _connectionString;
+
+        public RawDb(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;
+                await db.OpenAsync();
+
+                using (var cmd = CreateReadCommand(db))
+                {
+                    return await ReadSingleRow(db, cmd);
+                }
+            }
+        }
+
+        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
+            }
+        }
+
+        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;
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+                using (var cmd = CreateReadCommand(db))
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        result[i] = await ReadSingleRow(db, cmd);
+                        cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            var updateCommand = new StringBuilder(count);
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var updateCmd = db.CreateCommand())
+                using (var queryCmd = CreateReadCommand(db))
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        results[i] = await ReadSingleRow(db, queryCmd);
+                        queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
+
+                    // Postgres has problems with deadlocks when these aren't sorted
+                    Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                    for(int i = 0; i < count; i++)
+                    {
+                        var id = updateCmd.CreateParameter();
+                        id.ParameterName = BatchUpdateString.Strings[i].Id;
+                        id.DbType = DbType.Int32;
+                        updateCmd.Parameters.Add(id);
+
+                        var random = updateCmd.CreateParameter();
+                        random.ParameterName = BatchUpdateString.Strings[i].Random;
+                        random.DbType = DbType.Int32;
+                        updateCmd.Parameters.Add(random);
+
+                        var randomNumber = _random.Next(1, 10001);
+                        id.Value = results[i].Id;
+                        random.Value = randomNumber;
+                        results[i].RandomNumber = randomNumber;
+
+                        updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
+                    }
+
+                    updateCmd.CommandText = updateCommand.ToString();
+                    await updateCmd.ExecuteNonQueryAsync();
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<IEnumerable<Fortune>> LoadFortunesRows()
+        {
+            var result = new List<Fortune>();
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            using (var cmd = db.CreateCommand())
+            {
+                cmd.CommandText = "SELECT id, message FROM fortune";
+
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
+                {
+                    while (await rdr.ReadAsync())
+                    {
+                        result.Add(new Fortune
+                        {
+                            Id = rdr.GetInt32(0),
+                            Message = rdr.GetString(1)
+                        });
+                    }
+                }
+            }
+
+            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Sort();
+
+            return result;
+        }
+    }
+}

+ 17 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Data/World.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.ComponentModel.DataAnnotations.Schema;
+
+namespace Benchmarks.Data
+{
+    [Table("world")]
+    public class World
+    {
+        [Column("id")]
+        public int Id { get; set; }
+
+        [Column("randomnumber")]
+        public int RandomNumber { get; set; }
+    }
+}

+ 51 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesEfMiddleware.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 FortunesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesEf));
+
+        private readonly RequestDelegate _next;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesEfMiddleware(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<EfDb>();
+                var rows = await db.LoadFortunesRows();
+
+                await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class FortunesEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseFortunesEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<FortunesEfMiddleware>();
+        }
+    }
+}

+ 51 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/FortunesRawMiddleware.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 FortunesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesRaw));
+
+        private readonly RequestDelegate _next;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesRawMiddleware(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<RawDb>();
+                var rows = await db.LoadFortunesRows();
+
+                await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class FortunesRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseFortunesRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<FortunesRawMiddleware>();
+        }
+    }
+}

+ 56 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/JsonMiddleware.cs

@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+
+namespace Benchmarks.Middleware
+{
+    public class JsonMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.Json));
+        private static readonly JsonSerializer _json = new JsonSerializer();
+        private static readonly UTF8Encoding _encoding = new UTF8Encoding(false);
+        private const int _bufferSize = 27;
+
+        private readonly RequestDelegate _next;
+
+        public JsonMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                httpContext.Response.StatusCode = 200;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = _bufferSize;
+
+                using (var sw = new StreamWriter(httpContext.Response.Body, _encoding, bufferSize: _bufferSize))
+                {
+                    _json.Serialize(sw, new { message = "Hello, World!" });
+                }
+
+                return Task.CompletedTask;
+            }
+
+            return _next(httpContext);
+        }
+    }
+
+    public static class JsonMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseJson(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<JsonMiddleware>();
+        }
+    }
+}

+ 56 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MiddlewareHelpers.cs

@@ -0,0 +1,56 @@
+// 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.Globalization;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Http;
+
+namespace Benchmarks.Middleware
+{
+    public static class MiddlewareHelpers
+    {
+        public static int GetMultipleQueriesQueryCount(HttpContext httpContext)
+        {
+            var queries = 1;
+            var queriesRaw = httpContext.Request.Query["queries"];
+
+            if (queriesRaw.Count == 1)
+            {
+                int.TryParse(queriesRaw, out queries);
+            }
+
+            return queries > 500
+                ? 500
+                : queries > 0
+                    ? queries
+                    : 1;
+        }
+
+        public static async Task RenderFortunesHtml(IEnumerable<Fortune> model, HttpContext httpContext, HtmlEncoder htmlEncoder)
+        {
+            httpContext.Response.StatusCode = StatusCodes.Status200OK;
+            httpContext.Response.ContentType = "text/html; charset=UTF-8";
+
+            var sb = new StringBuilder();
+            sb.Append("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
+            foreach (var item in model)
+            {
+                sb.Append("<tr><td>");
+                sb.Append(item.Id.ToString(CultureInfo.InvariantCulture));
+                sb.Append("</td><td>");
+                sb.Append(htmlEncoder.Encode(item.Message));
+                sb.Append("</td></tr>");
+            }
+
+            sb.Append("</table></body></html>");
+            var response = sb.ToString();
+            // fortunes includes multibyte characters so response.Length is incorrect
+            httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(response);
+            await httpContext.Response.WriteAsync(response);
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesEfMiddleware.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 MultipleQueriesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiQueryEf));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleQueriesEfMiddleware(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<EfDb>();
+                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 MultipleQueriesEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleQueriesEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleQueriesEfMiddleware>();
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleQueriesRawMiddleware.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 MultipleQueriesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiQueryRaw));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleQueriesRawMiddleware(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<RawDb>();
+                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 MultipleQueriesRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleQueriesRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleQueriesRawMiddleware>();
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.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 MultipleUpdatesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateEf));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleUpdatesEfMiddleware(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<EfDb>();
+                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 MultipleUpdatesEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesEfMiddleware>();
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.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 MultipleUpdatesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateRaw));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleUpdatesRawMiddleware(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<RawDb>();
+                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 MultipleUpdatesRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesRawMiddleware>();
+        }
+    }
+}

+ 52 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/PlaintextMiddleware.cs

@@ -0,0 +1,52 @@
+// 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;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Benchmarks.Middleware
+{
+    public class PlaintextMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.Plaintext));
+        private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
+
+        private readonly RequestDelegate _next;
+
+        public PlaintextMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                return WriteResponse(httpContext.Response);
+            }
+
+            return _next(httpContext);
+        }
+
+        public static Task WriteResponse(HttpResponse response)
+        {
+            var payloadLength = _helloWorldPayload.Length;
+            response.StatusCode = 200;
+            response.ContentType = "text/plain";
+            response.ContentLength = payloadLength;
+            return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
+        }
+    }
+
+    public static class PlaintextMiddlewareExtensions
+    {
+        public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<PlaintextMiddleware>();
+        }
+    }
+}

+ 59 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryEfMiddleware.cs

@@ -0,0 +1,59 @@
+// 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 SingleQueryEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbSingleQueryEf));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public SingleQueryEfMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                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 SingleQueryEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseSingleQueryEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<SingleQueryEfMiddleware>();
+        }
+    }
+}

+ 60 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Middleware/SingleQueryRawMiddleware.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 SingleQueryRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbSingleQueryRaw));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public SingleQueryRawMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<RawDb>();
+                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 SingleQueryRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseSingleQueryRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<SingleQueryRawMiddleware>();
+        }
+    }
+}

+ 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="aspnetcore" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json" />
+  </packageSources>
+</configuration>

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

@@ -0,0 +1,75 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime;
+using System.Threading;
+using Benchmarks.Configuration;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks
+{
+    public class Program
+    {
+        public static string[] Args;
+
+        public static void Main(string[] args)
+        {
+            Args = args;
+
+            Console.WriteLine();
+            Console.WriteLine("ASP.NET Core Benchmarks");
+            Console.WriteLine("-----------------------");
+
+            Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
+            Console.WriteLine($"WebHostBuilder loading from: {typeof(WebHostBuilder).GetTypeInfo().Assembly.Location}");
+
+            var config = new ConfigurationBuilder()
+                .AddJsonFile("hosting.json", optional: true)
+                .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+                .AddCommandLine(args)
+                .Build();
+
+            var webHostBuilder = new WebHostBuilder()
+                .UseContentRoot(Directory.GetCurrentDirectory())
+                .UseConfiguration(config)
+                .UseStartup<Startup>()
+                .ConfigureServices(services => services
+                    .AddSingleton(new ConsoleArgs(args))
+                    .AddSingleton<IScenariosConfiguration, ConsoleHostScenariosConfiguration>()
+                    .AddSingleton<Scenarios>()
+                )
+                .UseDefaultServiceProvider(
+                    (context, options) => options.ValidateScopes = context.HostingEnvironment.IsDevelopment())
+                .UseKestrel();
+
+            var threadCount = GetThreadCount(config);
+
+            webHostBuilder.UseSockets(x =>
+            {
+                if (threadCount > 0)
+                {
+                    x.IOQueueCount = threadCount;
+                }
+
+                Console.WriteLine($"Using Sockets with {x.IOQueueCount} threads");
+            });
+
+            var webHost = webHostBuilder.Build();
+
+            Console.WriteLine($"Server GC is currently {(GCSettings.IsServerGC ? "ENABLED" : "DISABLED")}");
+
+            webHost.Run();
+        }
+
+        private static int GetThreadCount(IConfigurationRoot config)
+        {
+            var threadCountValue = config["threadCount"];
+            return threadCountValue == null ? -1 : int.Parse(threadCountValue);
+        }
+    }
+}

+ 184 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Startup.cs

@@ -0,0 +1,184 @@
+// 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.Data.Common;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Benchmarks.Middleware;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using MySql.Data.MySqlClient;
+using Npgsql;
+
+namespace Benchmarks
+{
+    public class Startup
+    {
+        public Startup(IHostingEnvironment hostingEnv, Scenarios scenarios)
+        {
+            // Set up configuration sources.
+            var builder = new ConfigurationBuilder()
+                .SetBasePath(hostingEnv.ContentRootPath)
+                .AddJsonFile("appsettings.json")
+                .AddJsonFile($"appsettings.{hostingEnv.EnvironmentName}.json", optional: true)
+                .AddEnvironmentVariables()
+                .AddCommandLine(Program.Args)
+                ;
+
+            Configuration = builder.Build();
+
+            Scenarios = scenarios;
+        }
+
+        public IConfigurationRoot Configuration { get; set; }
+
+        public Scenarios Scenarios { get; }
+
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.Configure<AppSettings>(Configuration);
+
+            // We re-register the Scenarios as an instance singleton here to avoid it being created again due to the
+            // registration done in Program.Main
+            services.AddSingleton(Scenarios);
+
+            // Common DB services
+            services.AddSingleton<IRandom, DefaultRandom>();
+
+            var appSettings = Configuration.Get<AppSettings>();
+            Console.WriteLine($"Database: {appSettings.Database}");
+
+            if (appSettings.Database == DatabaseServer.PostgreSql)
+            {
+                if (Scenarios.Any("Ef"))
+                {
+                    services.AddDbContextPool<ApplicationDbContext>(options => options.UseNpgsql(appSettings.ConnectionString));
+                }
+                
+                if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
+                {
+                    services.AddSingleton<DbProviderFactory>(NpgsqlFactory.Instance);
+                }
+            }
+            else if (appSettings.Database == DatabaseServer.MySql)
+            {
+                if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
+                {
+                    services.AddSingleton<DbProviderFactory>(MySqlClientFactory.Instance);
+                }
+            }
+
+            if (Scenarios.Any("Ef"))
+            {
+                services.AddScoped<EfDb>();
+            }
+
+            if (Scenarios.Any("Raw"))
+            {
+                services.AddScoped<RawDb>();
+            }
+
+            if (Scenarios.Any("Fortunes"))
+            {
+                var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+                settings.AllowCharacter('\u2014');  // allow EM DASH through
+                services.AddWebEncoders((options) =>
+                {
+                    options.TextEncoderSettings = settings;
+                });
+            }
+
+            if (Scenarios.Any("Mvc"))
+            {
+                var mvcBuilder = services
+                    .AddMvcCore()
+                    .AddControllersAsServices();
+
+                if (Scenarios.MvcJson || Scenarios.Any("MvcDbSingle") || Scenarios.Any("MvcDbMulti"))
+                {
+                    mvcBuilder.AddJsonFormatters();
+                }
+
+                if (Scenarios.MvcViews || Scenarios.Any("MvcDbFortunes"))
+                {
+                    mvcBuilder
+                        .AddViews()
+                        .AddRazorViewEngine();
+                }
+            }
+        }
+
+        public void Configure(IApplicationBuilder app)
+        {
+            if (Scenarios.Plaintext)
+            {
+                app.UsePlainText();
+            }
+
+            if (Scenarios.Json)
+            {
+                app.UseJson();
+            }
+
+            // Single query endpoints
+            if (Scenarios.DbSingleQueryRaw)
+            {
+                app.UseSingleQueryRaw();
+            }
+
+            if (Scenarios.DbSingleQueryEf)
+            {
+                app.UseSingleQueryEf();
+            }
+
+            // Multiple query endpoints
+            if (Scenarios.DbMultiQueryRaw)
+            {
+                app.UseMultipleQueriesRaw();
+            }
+
+            if (Scenarios.DbMultiQueryEf)
+            {
+                app.UseMultipleQueriesEf();
+            }
+
+            // Multiple update endpoints
+            if (Scenarios.DbMultiUpdateRaw)
+            {
+                app.UseMultipleUpdatesRaw();
+            }
+
+            if (Scenarios.DbMultiUpdateEf)
+            {
+                app.UseMultipleUpdatesEf();
+            }
+
+            // Fortunes endpoints
+            if (Scenarios.DbFortunesRaw)
+            {
+                app.UseFortunesRaw();
+            }
+
+            if (Scenarios.DbFortunesEf)
+            {
+                app.UseFortunesEf();
+            }
+
+            if (Scenarios.Any("Mvc"))
+            {
+                app.UseMvc();
+            }
+
+            if (Scenarios.StaticFiles)
+            {
+                app.UseStaticFiles();
+            }
+        }
+    }
+}

+ 12 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Fortunes/Fortunes.cshtml

@@ -0,0 +1,12 @@
+@using Benchmarks.Data
+@model IEnumerable<Fortune>
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body><table><tr><th>id</th><th>message</th></tr>
+    @foreach (var item in Model)
+    {
+        <tr><td>@item.Id</td><td>@item.Message</td></tr>
+    }
+</table></body>
+</html>

+ 5 - 0
frameworks/CSharp/aspnetcore-mono/Benchmarks/Views/Home/Index.cshtml

@@ -0,0 +1,5 @@
+@{
+    var message = "Hello, World!";
+}
+
+@message

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

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

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

@@ -0,0 +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",
+  "Database": "mysql"
+}

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

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3",
+  "Database": "postgresql"
+}

BIN
frameworks/CSharp/aspnetcore-mono/Benchmarks/wwwroot/1KB.txt


+ 50 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/AsciiString.cs

@@ -0,0 +1,50 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Text;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+namespace PlatformBenchmarks
+{
+    public readonly struct AsciiString : IEquatable<AsciiString>
+    {
+        private readonly byte[] _data;
+
+        public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s);
+
+        public int Length => _data.Length;
+
+        public ReadOnlySpan<byte> AsSpan() => _data;
+
+        public static implicit operator ReadOnlySpan<byte>(AsciiString str) => str._data;
+        public static implicit operator byte[] (AsciiString str) => str._data;
+
+        public static implicit operator AsciiString(string str) => new AsciiString(str);
+
+        public override string ToString() => HttpUtilities.GetAsciiStringNonNullCharacters(_data);
+        public static explicit operator string(AsciiString str) => str.ToString();
+
+        public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data);
+        private bool SequenceEqual(byte[] data1, byte[] data2) => new Span<byte>(data1).SequenceEqual(data2);
+
+        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 int GetHashCode()
+        {
+            // Copied from x64 version of string.GetLegacyNonRandomizedHashCode()
+            // https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/String.Comparison.cs
+            var data = _data;
+            int hash1 = 5381;
+            int hash2 = hash1;
+            foreach (int b in data)
+            {
+                hash1 = ((hash1 << 5) + hash1) ^ b;
+            }
+            return hash1 + (hash2 * 1566083941);
+        }
+
+    }
+}

+ 63 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Fortunes.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.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
+{
+    public partial class BenchmarkApplication
+    {
+        private async Task Fortunes(PipeWriter pipeWriter)
+        {
+            OutputFortunes(pipeWriter, await Db.LoadFortunesRows());
+        }
+
+        private void OutputFortunes(PipeWriter pipeWriter, List<Fortune> model)
+        {
+            var writer = GetWriter(pipeWriter);
+
+            // HTTP 1.1 OK
+            writer.Write(_http11OK);
+
+            // Server headers
+            writer.Write(_headerServer);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            // Content-Type header
+            writer.Write(_headerContentTypeHtml);
+
+            // Content-Length header
+            writer.Write(_headerContentLength);
+
+            var lengthWriter = writer;
+            writer.Write(_contentLengthGap);
+
+            // End of headers
+            writer.Write(_eoh);
+
+            var bodyStart = writer.Buffered;
+            // Body
+            writer.Write(_fortunesTableStart);
+            foreach (var item in model)
+            {
+                writer.Write(_fortunesRowStart);
+                writer.WriteNumeric((uint)item.Id);
+                writer.Write(_fortunesColumn);
+                writer.WriteUtf8HtmlString(item.Message);
+                writer.Write(_fortunesRowEnd);
+            }
+            writer.Write(_fortunesTableEnd);
+            lengthWriter.WriteNumeric((uint)(writer.Buffered - bodyStart));
+
+            writer.Commit();
+        }
+    }
+}

+ 181 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs

@@ -0,0 +1,181 @@
+// 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.IO.Pipelines;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication : IHttpConnection
+    {
+        private State _state;
+
+        public PipeReader Reader { get; set; }
+        public PipeWriter Writer { get; set; }
+
+        private HttpParser<ParsingAdapter> Parser { get; } = new HttpParser<ParsingAdapter>();
+
+        public async Task ExecuteAsync()
+        {
+            try
+            {
+                await ProcessRequestsAsync();
+
+                Reader.Complete();
+            }
+            catch (Exception ex)
+            {
+                Reader.Complete(ex);
+            }
+            finally
+            {
+                Writer.Complete();
+            }
+        }
+
+        private async Task ProcessRequestsAsync()
+        {
+            while (true)
+            {
+                var task = Reader.ReadAsync();
+
+                if (!task.IsCompleted)
+                {
+                    // No more data in the input
+                    await OnReadCompletedAsync();
+                }
+
+                var result = await task;
+                var buffer = result.Buffer;
+                while (true)
+                {
+                    if (!ParseHttpRequest(ref buffer, result.IsCompleted, out var examined))
+                    {
+                        return;
+                    }
+
+                    if (_state == State.Body)
+                    {
+                        await ProcessRequestAsync();
+
+                        _state = State.StartLine;
+
+                        if (!buffer.IsEmpty)
+                        {
+                            // More input data to parse
+                            continue;
+                        }
+                    }
+
+                    // No more input or incomplete data, Advance the Reader
+                    Reader.AdvanceTo(buffer.Start, examined);
+                    break;
+                }
+            }
+        }
+
+        private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted, out SequencePosition examined)
+        {
+            examined = buffer.End;
+
+            var consumed = buffer.Start;
+            var state = _state;
+
+            if (!buffer.IsEmpty)
+            {
+                if (state == State.StartLine)
+                {
+                    if (Parser.ParseRequestLine(new ParsingAdapter(this), buffer, out consumed, out examined))
+                    {
+                        state = State.Headers;
+                    }
+
+                    buffer = buffer.Slice(consumed);
+                }
+
+                if (state == State.Headers)
+                {
+                    if (Parser.ParseHeaders(new ParsingAdapter(this), buffer, out consumed, out examined, out int consumedBytes))
+                    {
+                        state = State.Body;
+                    }
+
+                    buffer = buffer.Slice(consumed);
+                }
+
+                if (state != State.Body && isCompleted)
+                {
+                    ThrowUnexpectedEndOfData();
+                }
+            }
+            else if (isCompleted)
+            {
+                return false;
+            }
+
+            _state = state;
+            return true;
+        }
+
+        public void OnHeader(Span<byte> name, Span<byte> value)
+        {
+        }
+
+        public async ValueTask OnReadCompletedAsync()
+        {
+            await Writer.FlushAsync();
+        }
+
+        private static void ThrowUnexpectedEndOfData()
+        {
+            throw new InvalidOperationException("Unexpected end of data!");
+        }
+
+        private enum State
+        {
+            StartLine,
+            Headers,
+            Body
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static BufferWriter<WriterAdapter> GetWriter(PipeWriter pipeWriter)
+            => new BufferWriter<WriterAdapter>(new WriterAdapter(pipeWriter));
+
+        private struct WriterAdapter : IBufferWriter<byte>
+        {
+            public PipeWriter Writer;
+
+            public WriterAdapter(PipeWriter writer)
+                => Writer = writer;
+
+            public void Advance(int count)
+                => Writer.Advance(count);
+
+            public Memory<byte> GetMemory(int sizeHint = 0)
+                => Writer.GetMemory(sizeHint);
+
+            public Span<byte> GetSpan(int sizeHint = 0)
+                => Writer.GetSpan(sizeHint);
+        }
+
+        private struct ParsingAdapter : IHttpRequestLineHandler, IHttpHeadersHandler
+        {
+            public BenchmarkApplication RequestHandler;
+
+            public ParsingAdapter(BenchmarkApplication requestHandler)
+                => RequestHandler = requestHandler;
+
+            public void OnHeader(Span<byte> name, Span<byte> value)
+                => RequestHandler.OnHeader(name, value);
+
+            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);
+        }
+    }
+
+}

+ 45 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.Json.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Utf8Json;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private static void Json(PipeWriter pipeWriter)
+        {
+            var writer = GetWriter(pipeWriter);
+
+            // HTTP 1.1 OK
+            writer.Write(_http11OK);
+
+            // Server headers
+            writer.Write(_headerServer);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            // Content-Type header
+            writer.Write(_headerContentTypeJson);
+
+            // Content-Length header
+            writer.Write(_headerContentLength);
+            var jsonPayload = JsonSerializer.SerializeUnsafe(new { message = "Hello, World!" });
+            writer.WriteNumeric((uint)jsonPayload.Count);
+
+            // End of headers
+            writer.Write(_eoh);
+
+            // Body
+            writer.Write(jsonPayload);
+            writer.Commit();
+        }
+    }
+}

+ 44 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.PlainText.cs

@@ -0,0 +1,44 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Utf8Json;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private static void PlainText(PipeWriter pipeWriter)
+        {
+            var writer = GetWriter(pipeWriter);
+
+            // HTTP 1.1 OK
+            writer.Write(_http11OK);
+
+            // Server headers
+            writer.Write(_headerServer);
+
+            // Date header
+            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
+            writer.Write(_plainTextBody);
+            writer.Commit();
+        }
+    }
+}

+ 122 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkApplication.cs

@@ -0,0 +1,122 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Utf8Json;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        private readonly static AsciiString _applicationName = "Kestrel Platform-Level Application";
+        public static AsciiString ApplicationName => _applicationName;
+
+        private readonly static AsciiString _crlf = "\r\n";
+        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 _headerServer = "Server: Custom";
+        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 _plainTextBody = "Hello, World!";
+
+        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 _fortunesColumn = "</td><td>";
+        private readonly static AsciiString _fortunesRowEnd = "</td></tr>";
+        private readonly static AsciiString _fortunesTableEnd = "</table></body></html>";
+        private readonly static AsciiString _contentLengthGap = new string(' ', 4);
+
+        public static IDb Db { get; set; }
+
+        public static class Paths
+        {
+            public readonly static AsciiString Plaintext = "/plaintext";
+            public readonly static AsciiString Json = "/json";
+            public readonly static AsciiString Fortunes = "/fortunes";
+        }
+
+        private RequestType _requestType;
+
+        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)
+            {
+                if (Paths.Plaintext.Length <= path.Length && path.StartsWith(Paths.Plaintext))
+                {
+                    requestType = RequestType.PlainText;
+                }
+                else if (Paths.Json.Length <= path.Length && path.StartsWith(Paths.Json))
+                {
+                    requestType = RequestType.Json;
+                }
+                else if (Paths.Fortunes.Length <= path.Length && path.StartsWith(Paths.Fortunes))
+                {
+                    requestType = RequestType.Fortunes;
+                }
+            }
+
+            _requestType = requestType;
+        }
+
+        public Task ProcessRequestAsync()
+        {
+            if (_requestType == RequestType.PlainText)
+            {
+                PlainText(Writer);
+            }
+            else if (_requestType == RequestType.Json)
+            {
+                Json(Writer);
+            }
+            else if (_requestType == RequestType.Fortunes)
+            {
+                return Fortunes(Writer);
+            }
+            else
+            {
+                Default(Writer);
+            }
+
+            return Task.CompletedTask;
+        }
+
+        private static void Default(PipeWriter pipeWriter)
+        {
+            var writer = GetWriter(pipeWriter);
+
+            // HTTP 1.1 OK
+            writer.Write(_http11OK);
+
+            // Server headers
+            writer.Write(_headerServer);
+
+            // Date header
+            writer.Write(DateHeader.HeaderBytes);
+
+            // Content-Length 0
+            writer.Write(_headerContentLengthZero);
+
+            // End of headers
+            writer.Write(_crlf);
+            writer.Commit();
+        }
+
+        private enum RequestType
+        {
+            NotRecognized,
+            PlainText,
+            Json,
+            Fortunes
+        }
+    }
+}

+ 81 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs

@@ -0,0 +1,81 @@
+// 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.Net;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.Extensions.Configuration;
+
+namespace PlatformBenchmarks
+{
+    public static class BenchmarkConfigurationHelpers
+    {
+        public static IWebHostBuilder UseBenchmarksConfiguration(this IWebHostBuilder builder, IConfiguration 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.UseLibuv(options =>
+                {
+                    if (theadCount.HasValue)
+                    {
+                        options.ThreadCount = theadCount.Value;
+                    }
+                });
+            }
+            else if (string.Equals(webHost, "Sockets", StringComparison.OrdinalIgnoreCase))
+            {
+                builder.UseSockets(options =>
+                {
+                    if (theadCount.HasValue)
+                    {
+                        options.IOQueueCount = theadCount.Value;
+                    }
+                });
+            }
+
+            return builder;
+        }
+        
+        public static IPEndPoint CreateIPEndPoint(this IConfiguration config)
+        {
+            var url = config["server.urls"] ?? config["urls"];
+
+            if (string.IsNullOrEmpty(url))
+            {
+                return new IPEndPoint(IPAddress.Loopback, 8080);
+            }
+
+            var address = ServerAddress.FromUrl(url);
+
+            IPAddress ip;
+
+            if (string.Equals(address.Host, "localhost", StringComparison.OrdinalIgnoreCase))
+            {
+                ip = IPAddress.Loopback;
+            }
+            else if (!IPAddress.TryParse(address.Host, out ip))
+            {
+                ip = IPAddress.IPv6Any;
+            }
+
+            return new IPEndPoint(ip, address.Port);
+        }
+    }
+}

+ 12 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Configuration/AppSettings.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace PlatformBenchmarks
+{
+    public class AppSettings
+    {
+        public string ConnectionString { get; set; }
+
+        public DatabaseServer Database { get; set; } = DatabaseServer.None;
+    }
+}

+ 13 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Configuration/DatabaseServer.cs

@@ -0,0 +1,13 @@
+// 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 PlatformBenchmarks
+{
+    public enum DatabaseServer
+    {
+        None,
+        SqlServer,
+        PostgreSql,
+        MySql
+    }
+}

+ 24 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/BatchUpdateString.cs

@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PlatformBenchmarks
+{
+    internal class BatchUpdateString
+    {
+        public static IList<BatchUpdateString> Strings { get;} = 
+            Enumerable.Range(0, 500)
+                      .Select(i => new BatchUpdateString
+                      {
+                          Id = $"Id_{i}",
+                          Random = $"Random_{i}",
+                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                      }).ToArray();
+                        
+        public string Id { get; set; }
+        public string Random { get; set; }
+        public string UpdateQuery { get; set; }
+    }
+}

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

@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+
+namespace PlatformBenchmarks
+{
+    public class Fortune : IComparable<Fortune>, IComparable
+    {
+        public int Id { get; set; }
+
+        public string Message { get; set; }
+        
+        public int CompareTo(object obj)
+        {
+            return CompareTo((Fortune)obj);
+        }
+
+        public int CompareTo(Fortune other)
+        {
+            // Performance critical, using culture insensitive comparison
+            return String.CompareOrdinal(Message, other.Message);
+        }
+    }
+}

+ 19 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/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 PlatformBenchmarks
+{
+    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/PlatformBenchmarks/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 PlatformBenchmarks
+{
+    public interface IRandom
+    {
+        int Next(int minValue, int maxValue);
+    }
+}

+ 20 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/Random.cs

@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading;
+
+namespace PlatformBenchmarks
+{
+    public class DefaultRandom : IRandom
+    {
+        private static int nextSeed = 0;
+        // Random isn't thread safe
+        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
+
+        public int Next(int minValue, int maxValue)
+        {
+            return _random.Value.Next(minValue, maxValue);
+        }
+    }
+}

+ 171 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Data/RawDb.cs

@@ -0,0 +1,171 @@
+// 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;
+using System.Data.Common;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+
+namespace PlatformBenchmarks
+{
+    public class RawDb : IDb
+    {
+        private readonly IRandom _random;
+        private readonly DbProviderFactory _dbProviderFactory;
+        private readonly string _connectionString;
+
+        public RawDb(IRandom random, DbProviderFactory dbProviderFactory, AppSettings appSettings)
+        {
+            _random = random;
+            _dbProviderFactory = dbProviderFactory;
+            _connectionString = appSettings.ConnectionString;
+        }
+
+        public async Task<World> LoadSingleQueryRow()
+        {
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var cmd = CreateReadCommand(db))
+                {
+                    return await ReadSingleRow(db, cmd);
+                }
+            }
+        }
+
+        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
+            }
+        }
+
+        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;
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+                using (var cmd = CreateReadCommand(db))
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        result[i] = await ReadSingleRow(db, cmd);
+                        cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            var updateCommand = new StringBuilder(count);
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var updateCmd = db.CreateCommand())
+                using (var queryCmd = CreateReadCommand(db))
+                {
+                    for (int i = 0; i < count; i++)
+                    {
+                        results[i] = await ReadSingleRow(db, queryCmd);
+                        queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                    }
+
+                    // Postgres has problems with deadlocks when these aren't sorted
+                    Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                    for(int i = 0; i < count; i++)
+                    {
+                        var id = updateCmd.CreateParameter();
+                        id.ParameterName = BatchUpdateString.Strings[i].Id;
+                        id.DbType = DbType.Int32;
+                        updateCmd.Parameters.Add(id);
+
+                        var random = updateCmd.CreateParameter();
+                        random.ParameterName = BatchUpdateString.Strings[i].Random;
+                        random.DbType = DbType.Int32;
+                        updateCmd.Parameters.Add(random);
+
+                        var randomNumber = _random.Next(1, 10001);
+                        id.Value = results[i].Id;
+                        random.Value = randomNumber;
+                        results[i].RandomNumber = randomNumber;
+
+                        updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
+                    }
+
+                    updateCmd.CommandText = updateCommand.ToString();
+                    await updateCmd.ExecuteNonQueryAsync();
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<List<Fortune>> LoadFortunesRows()
+        {
+            var result = new List<Fortune>();
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            using (var cmd = db.CreateCommand())
+            {
+                cmd.CommandText = "SELECT id, message FROM fortune";
+
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
+                {
+                    while (await rdr.ReadAsync())
+                    {
+                        result.Add(new Fortune
+                        {
+                            Id = rdr.GetInt32(0),
+                            Message = rdr.GetString(1)
+                        });
+                    }
+                }
+            }
+
+            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Sort();
+
+            return result;
+        }
+    }
+}

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

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace PlatformBenchmarks
+{
+    public class World
+    {
+        public int Id { get; set; }
+        
+        public int RandomNumber { get; set; }
+    }
+}

+ 29 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/HttpApplication.cs

@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+
+namespace PlatformBenchmarks
+{
+    public static class HttpApplicationConnectionBuilderExtensions
+    {
+        public static IConnectionBuilder UseHttpApplication<TConnection>(this IConnectionBuilder builder) where TConnection : IHttpConnection, new()
+        {
+            return builder.Use(next => new HttpApplication<TConnection>().ExecuteAsync);
+        }
+    }
+
+    public class HttpApplication<TConnection> where TConnection : IHttpConnection, new()
+    {
+        public Task ExecuteAsync(ConnectionContext connection)
+        {
+            var httpConnection = new TConnection
+            {
+                Reader = connection.Transport.Input,
+                Writer = connection.Transport.Output
+            };
+            return httpConnection.ExecuteAsync();
+        }
+    }
+}

+ 17 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/IHttpConnection.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.IO.Pipelines;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+
+namespace PlatformBenchmarks
+{
+    public interface IHttpConnection : IHttpHeadersHandler, IHttpRequestLineHandler
+    {
+        PipeReader Reader { get; set; }
+        PipeWriter Writer { get; set; }
+        Task ExecuteAsync();
+        ValueTask OnReadCompletedAsync();
+    }
+}

+ 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="aspnetcore" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json" />
+  </packageSources>
+</configuration>

+ 38 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/PlatformBenchmarks.csproj

@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net471</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <LangVersion>7.3</LangVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
+    <!-- Add the myget feed, without altering any other configurations. -->
+    <RestoreAdditionalProjectSources>https://dotnet.myget.org/F/dotnet-core/api/v3/index.json</RestoreAdditionalProjectSources>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="2.1.0-rc1-final" />
+    <!-- Fix https://github.com/aspnet/KestrelHttpServer/pull/2562  -->
+    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" Version="2.1.0-rtm-30793" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0-rc1-final" />
+    <PackageReference Include="Utf8Json" Version="1.3.7" />
+    <PackageReference Include="Npgsql" Version="4.0.0-rc1" />
+    <PackageReference Include="MySqlConnector" Version="0.40.3" />
+  </ItemGroup>
+  
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net471' ">
+    <!-- Make .NET 4.7.1 reference assemblies available even on Linux -->
+    <PackageReference Include="Microsoft.TargetingPack.NETFramework.v4.7.1" Version="1.0.0" ExcludeAssets="All" PrivateAssets="All" />
+    <Reference Include="netstandard" />
+    <Reference Include="System.Runtime" />
+    <Reference Include="System.Threading.Tasks" />
+    <PackageReference Include="NETStandard.Library" Version="2.0.0" />
+  </ItemGroup>
+  
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net471' ">
+    <!-- Make sure the reference assemblies are available -->
+    <FrameworkPathOverride>$(NuGetPackageRoot)microsoft.targetingpack.netframework.v4.7.1\1.0.0\lib\net471\</FrameworkPathOverride>
+  </PropertyGroup>
+</Project>

+ 68 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Program.cs

@@ -0,0 +1,68 @@
+// 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.Net;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Microsoft.Extensions.Configuration;
+using Npgsql;
+using MySql.Data.MySqlClient;
+
+namespace PlatformBenchmarks
+{
+    public class Program
+    {
+        public static string[] Args;
+
+        public static void Main(string[] args)
+        {
+            Args = args;
+
+            Console.WriteLine(BenchmarkApplication.ApplicationName);
+            Console.WriteLine(BenchmarkApplication.Paths.Plaintext);
+            Console.WriteLine(BenchmarkApplication.Paths.Json);
+            Console.WriteLine(BenchmarkApplication.Paths.Fortunes);
+            DateHeader.SyncDateTimer();
+
+            BuildWebHost(args).Run();
+        }
+
+        public static IWebHost BuildWebHost(string[] args)
+        {
+            var config = new ConfigurationBuilder()
+                .AddJsonFile("appsettings.json")
+                .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+                .AddCommandLine(args)
+                .Build();
+
+            var appSettings = config.Get<AppSettings>();
+            Console.WriteLine($"Database: {appSettings.Database}");
+
+            if (appSettings.Database == DatabaseServer.PostgreSql)
+            {
+                BenchmarkApplication.Db = new RawDb(new DefaultRandom(), NpgsqlFactory.Instance, appSettings);
+            }
+            else if (appSettings.Database == DatabaseServer.MySql)
+            {
+                BenchmarkApplication.Db = new RawDb(new DefaultRandom(), MySqlClientFactory.Instance, appSettings);
+            }
+
+            var host = new WebHostBuilder()
+                .UseBenchmarksConfiguration(config)
+                .UseKestrel((context, options) =>
+                {
+                    IPEndPoint endPoint = context.Configuration.CreateIPEndPoint();
+
+                    options.Listen(endPoint, builder =>
+                    {
+                        builder.UseHttpApplication<BenchmarkApplication>();
+                    });
+                })
+                .UseStartup<Startup>()
+                .Build();
+
+            return host;
+        }
+    }
+}

+ 16 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Startup.cs

@@ -0,0 +1,16 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Builder;
+
+namespace PlatformBenchmarks
+{
+    public class Startup
+    {
+        public void Configure(IApplicationBuilder app)
+        {
+            
+        }
+    }
+}

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

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

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

@@ -0,0 +1,332 @@
+// 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;
+        }
+    }
+}

+ 95 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/BufferWriter.cs

@@ -0,0 +1,95 @@
+// 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;
+
+namespace PlatformBenchmarks
+{
+    public ref struct BufferWriter<T> where T : IBufferWriter<byte>
+    {
+        private T _output;
+        private Span<byte> _span;
+        private int _buffered;
+
+        public BufferWriter(T output)
+        {
+            _buffered = 0;
+            _output = output;
+            _span = output.GetSpan();
+        }
+
+        public Span<byte> Span => _span;
+        public int Buffered => _buffered;
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Commit()
+        {
+            var buffered = _buffered;
+            if (buffered > 0)
+            {
+                _buffered = 0;
+                _output.Advance(buffered);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Advance(int count)
+        {
+            _buffered += count;
+            _span = _span.Slice(count);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Write(ReadOnlySpan<byte> source)
+        {
+            if (_span.Length >= source.Length)
+            {
+                source.CopyTo(_span);
+                Advance(source.Length);
+            }
+            else
+            {
+                WriteMultiBuffer(source);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Ensure(int count = 1)
+        {
+            if (_span.Length < count)
+            {
+                EnsureMore(count);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void EnsureMore(int count = 0)
+        {
+            if (_buffered > 0)
+            {
+                Commit();
+            }
+
+            _output.GetMemory(count);
+            _span = _output.GetSpan();
+        }
+
+        private void WriteMultiBuffer(ReadOnlySpan<byte> source)
+        {
+            while (source.Length > 0)
+            {
+                if (_span.Length == 0)
+                {
+                    EnsureMore();
+                }
+
+                var writable = Math.Min(source.Length, _span.Length);
+                source.Slice(0, writable).CopyTo(_span);
+                source = source.Slice(writable);
+                Advance(writable);
+            }
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-mono/PlatformBenchmarks/Utilities/DateHeader.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.Buffers.Text;
+using System.Diagnostics;
+using System.Text;
+using System.Threading;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
+{
+    /// <summary>
+    /// Manages the generation of the date header value.
+    /// </summary>
+    internal static class DateHeader 
+    {
+        const int prefixLength = 8; // "\r\nDate: ".Length
+        const int dateTimeRLength = 29; // Wed, 14 Mar 2018 14:20:00 GMT
+        const int suffixLength = 2; // crlf
+        const int suffixIndex = dateTimeRLength + prefixLength;
+
+        private static readonly Timer s_timer = new Timer((s) => {
+            SetDateValues(DateTimeOffset.UtcNow);
+        }, null, 1000, 1000);
+
+        private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + suffixLength];
+        private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + suffixLength];
+
+        static DateHeader()
+        {
+            var utf8 = Encoding.ASCII.GetBytes("\r\nDate: ").AsSpan();
+            utf8.CopyTo(s_headerBytesMaster);
+            utf8.CopyTo(s_headerBytesScratch);
+            s_headerBytesMaster[suffixIndex] = (byte)'\r';
+            s_headerBytesMaster[suffixIndex + 1] = (byte)'\n';
+            s_headerBytesScratch[suffixIndex] = (byte)'\r';
+            s_headerBytesScratch[suffixIndex + 1] = (byte)'\n';
+            SetDateValues(DateTimeOffset.UtcNow);
+            SyncDateTimer();
+        }
+
+        public static void SyncDateTimer() => s_timer.Change(1000, 1000);
+
+        public static ReadOnlySpan<byte> HeaderBytes => s_headerBytesMaster;
+
+        private static void SetDateValues(DateTimeOffset value)
+        {
+            lock (s_headerBytesScratch)
+            {
+                if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan(prefixLength), out int written, 'R'))
+                {
+                    throw new Exception("date time format failed");
+                }
+                Debug.Assert(written == dateTimeRLength);
+                var temp = s_headerBytesMaster;
+                s_headerBytesMaster = s_headerBytesScratch;
+                s_headerBytesScratch = temp;
+            }
+        }
+    }
+}

+ 55 - 0
frameworks/CSharp/aspnetcore-mono/README.md

@@ -0,0 +1,55 @@
+# ASP.NET Core Tests on Mono and Linux
+
+See [ASP.NET Core](https://github.com/aspnet) and [Mono](https://www.mono-project.com/) for more information.
+
+This includes tests for plaintext and json serialization.
+
+## Infrastructure Software Versions
+
+**Language**
+
+* C# 7.2
+
+**Platforms**
+
+* Mono (Linux)
+
+**Web Servers**
+
+* [Kestrel](https://github.com/aspnet/KestrelHttpServer)
+
+**Web Stack**
+
+* ASP.NET Core
+* ASP.NET Core MVC
+
+## Paths & Source for Tests
+
+### Platform
+
+* [Plaintext](PlatformBenchmarks/BenchmarkApplication.Plaintext.cs): "/plaintext"
+* [JSON Serialization](PlatformBenchmarks/BenchmarkApplication.Json.cs): "/json"
+* [Fortunes](PlatformBenchmarks/BenchmarkApplication.Fortunes.cs): "/fortunes"
+
+### Framework
+
+* [Plaintext](Benchmarks/Middleware/PlaintextMiddleware.cs): "/plaintext"
+* [Plaintext MVC](Benchmarks/Controllers/HomeController.cs): "/mvc/plaintext"
+* [JSON Serialization](Benchmarks/Middleware/JsonMiddleware.cs): "/json"
+* [JSON Serialization MVC](Benchmarks/Controllers/HomeController.cs): "/mvc/json"
+* [Single Query Raw](Benchmarks/Middleware/SingleQueryRawMiddleware.cs): "/db/raw"
+* [Single Query EF](Benchmarks/Middleware/SingleQueryEfMiddleware.cs): "/db/ef"
+* [Single Query MVC Raw](Benchmarks/Controllers/SingleQueryController.cs): "/mvc/db/raw"
+* [Single Query MVC EF](Benchmarks/Controllers/SingleQueryController.cs): "/mvc/db/ef"
+* [Multiple Queries Raw](Benchmarks/Middleware/MultipleQueriesRawMiddleware.cs): "/queries/raw"
+* [Multiple Queries EF](Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs): "/queries/ef"
+* [Multiple Queries MVC Raw](Benchmarks/Controllers/MultipleQueriesController.cs): "/mvc/queries/raw"
+* [Multiple Queries MVC EF](Benchmarks/Controllers/MultipleQueriesController.cs): "/mvc/queries/ef"
+* [Data Updates Raw](Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs): "/updates/raw"
+* [Data Updates EF](Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs): "/updates/ef"
+* [Data Updates MVC Raw](Benchmarks/Controllers/MultipleUpdatesController.cs): "/mvc/updates/raw"
+* [Data Updates MVC EF](Benchmarks/Controllers/MultipleUpdatesController.cs): "/mvc/updates/ef"
+* [Fortunes Raw](Benchmarks/Middleware/FortunesRawMiddleware.cs): "/fortunes/raw"
+* [Fortunes EF](Benchmarks/Middleware/FortunesEfMiddleware.cs): "/fortunes/ef"
+* [Fortunes MVC Raw](Benchmarks/Controllers/FortunesController.cs): "/mvc/fortunes/raw"
+* [Fortunes MVC EF](Benchmarks/Controllers/FortunesController.cs): "/mvc/fortunes/ef"

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

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

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

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

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

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

+ 11 - 0
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mvc.dockerfile

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

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

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

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

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

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

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

+ 11 - 0
frameworks/CSharp/aspnetcore-mono/aspcore-mono-mw.dockerfile

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

+ 13 - 0
frameworks/CSharp/aspnetcore-mono/aspcore-mono-my.dockerfile

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

+ 13 - 0
frameworks/CSharp/aspnetcore-mono/aspcore-mono-pg.dockerfile

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

+ 13 - 0
frameworks/CSharp/aspnetcore-mono/aspcore-mono.dockerfile

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

+ 224 - 0
frameworks/CSharp/aspnetcore-mono/benchmark_config.json

@@ -0,0 +1,224 @@
+{
+  "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",
+      "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",
+      "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-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"
+    },
+    "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-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"
+    }
+  }]
+}