Forráskód Böngészése

aspnetcore test automation (#2078)

* aspnetcore test automation

* add Benchmarks code to repo

* respond to feedback
nathana1 9 éve
szülő
commit
38d1429756
66 módosított fájl, 2490 hozzáadás és 0 törlés
  1. 1 0
      .travis.yml
  2. 19 0
      frameworks/CSharp/aspnetcore/Benchmarks/Benchmarks.xproj
  3. 11 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs
  4. 15 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/ConsoleArgs.cs
  5. 85 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/ConsoleHostScenariosConfiguration.cs
  6. 20 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/EnabledScenario.cs
  7. 10 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/IScenariosConfiguration.cs
  8. 149 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs
  9. 75 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs
  10. 46 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/HomeController.cs
  11. 28 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs
  12. 89 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbSeeder.cs
  13. 79 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs
  14. 52 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs
  15. 33 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs
  16. 10 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/IRandom.cs
  17. 17 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs
  18. 122 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs
  19. 12 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs
  20. 90 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/DebugInfoPageMiddleware.cs
  21. 47 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs
  22. 51 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesDapperMiddleware.cs
  23. 51 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs
  24. 51 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesRawMiddleware.cs
  25. 57 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/JsonMiddleware.cs
  26. 47 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MiddlewareHelpers.cs
  27. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesDapperMiddleware.cs
  28. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs
  29. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesRawMiddleware.cs
  30. 48 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/PlaintextMiddleware.cs
  31. 60 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryDapperMiddleware.cs
  32. 59 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs
  33. 60 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryRawMiddleware.cs
  34. 31 0
      frameworks/CSharp/aspnetcore/Benchmarks/Migrations/20151113004227_Initial.Designer.cs
  35. 32 0
      frameworks/CSharp/aspnetcore/Benchmarks/Migrations/20151113004227_Initial.cs
  36. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Migrations/20151124205054_Fortune.Designer.cs
  37. 32 0
      frameworks/CSharp/aspnetcore/Benchmarks/Migrations/20151124205054_Fortune.cs
  38. 41 0
      frameworks/CSharp/aspnetcore/Benchmarks/Migrations/ApplicationDbContextModelSnapshot.cs
  39. 8 0
      frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config
  40. 138 0
      frameworks/CSharp/aspnetcore/Benchmarks/Program.cs
  41. 27 0
      frameworks/CSharp/aspnetcore/Benchmarks/Properties/launchSettings.json
  42. 185 0
      frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs
  43. 12 0
      frameworks/CSharp/aspnetcore/Benchmarks/Views/Fortunes/Fortunes.cshtml
  44. 5 0
      frameworks/CSharp/aspnetcore/Benchmarks/Views/Home/Index.cshtml
  45. 3 0
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json
  46. 55 0
      frameworks/CSharp/aspnetcore/Benchmarks/project.json
  47. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/128B.txt
  48. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/16KB.txt
  49. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/1KB.txt
  50. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/1MB.txt
  51. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/4KB.txt
  52. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/512B.txt
  53. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/512KB.txt
  54. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/5MB.txt
  55. BIN
      frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/favicon.ico
  56. 32 0
      frameworks/CSharp/aspnetcore/README.md
  57. 119 0
      frameworks/CSharp/aspnetcore/benchmark_config.json
  58. 8 0
      frameworks/CSharp/aspnetcore/run-linux.sh
  59. 8 0
      frameworks/CSharp/aspnetcore/run-windows.ps1
  60. 3 0
      frameworks/CSharp/aspnetcore/setup-mvc-weblistener.sh
  61. 3 0
      frameworks/CSharp/aspnetcore/setup-mvc-windows.sh
  62. 3 0
      frameworks/CSharp/aspnetcore/setup-mvc.sh
  63. 3 0
      frameworks/CSharp/aspnetcore/setup-weblistener.sh
  64. 3 0
      frameworks/CSharp/aspnetcore/setup-windows.sh
  65. 3 0
      frameworks/CSharp/aspnetcore/setup.sh
  66. 17 0
      toolset/setup/linux/languages/dotnetcore.sh

+ 1 - 0
.travis.yml

@@ -22,6 +22,7 @@ env:
     - "TESTDIR=C/onion"
     - "TESTDIR=C/h2o"
     - "TESTDIR=CSharp/aspnet"
+    - "TESTDIR=CSharp/aspnetcore"
     ## - "TESTDIR=CSharp/aspnet-stripped"
     - "TESTDIR=CSharp/evhttp-sharp"
     ## - "TESTDIR=CSharp/HttpListener"

+ 19 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Benchmarks.xproj

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>ec62acf4-8b19-41c2-b699-d75cab7763df</ProjectGuid>
+    <RootNamespace>Benchmarks</RootNamespace>
+    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
+    <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SchemaVersion>2.0</SchemaVersion>
+    <DevelopmentServerPort>13007</DevelopmentServerPort>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project>

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

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

+ 15 - 0
frameworks/CSharp/aspnetcore/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/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();
+        }
+    }
+}

+ 20 - 0
frameworks/CSharp/aspnetcore/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/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);
+    }
+}

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

@@ -0,0 +1,149 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+using System.Linq.Expressions;
+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("/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/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.IndexOf(partialName, StringComparison.OrdinalIgnoreCase) >= 0)
+                .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; }
+    }
+}

+ 75 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.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.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc")]
+    public class FortunesController : Controller
+    {
+        private RawDb _rawDb;
+        private DapperDb _dapperDb;
+        private EfDb _efDb;
+
+        private RawDb RawDb
+        {
+            get
+            {
+                if (_rawDb == null)
+                {
+                    _rawDb = HttpContext.RequestServices.GetRequiredService<RawDb>();
+                }
+
+                return _rawDb;
+            }
+        }
+
+        private DapperDb DapperDb
+        {
+            get
+            {
+                if (_dapperDb == null)
+                {
+                    _dapperDb = HttpContext.RequestServices.GetRequiredService<DapperDb>();
+                }
+
+                return _dapperDb;
+            }
+        }
+
+        private EfDb EfDb
+        {
+            get
+            {
+                if (_efDb == null)
+                {
+                    _efDb = HttpContext.RequestServices.GetRequiredService<EfDb>();
+                }
+
+                return _efDb;
+            }
+        }
+
+        [HttpGet("fortunes/raw")]
+        public async Task<IActionResult> Raw()
+        {
+            return View("Fortunes", await RawDb.LoadFortunesRows());
+        }
+
+        [HttpGet("fortunes/dapper")]
+        public async Task<IActionResult> Dapper()
+        {
+            return View("Fortunes", await DapperDb.LoadFortunesRows());
+        }
+
+        [HttpGet("fortunes/ef")]
+        public async Task<IActionResult> Ef()
+        {
+            return View("Fortunes", await EfDb.LoadFortunesRows());
+        }
+    }
+}

+ 46 - 0
frameworks/CSharp/aspnetcore/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);
+            }
+        }
+    }
+}

+ 28 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.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 Benchmarks.Configuration;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+
+namespace Benchmarks.Data
+{
+    public class ApplicationDbContext : DbContext
+    {
+        private readonly AppSettings _appSettings;
+
+        public ApplicationDbContext(IOptions<AppSettings> appSettings)
+        {
+            _appSettings = appSettings.Value;
+        }
+
+        public DbSet<World> World { get; set; }
+
+        public DbSet<Fortune> Fortune { get; set; }
+
+        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+        {
+            optionsBuilder.UseSqlServer(_appSettings.ConnectionString);
+        }
+    }
+}

+ 89 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbSeeder.cs

@@ -0,0 +1,89 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Linq;
+
+namespace Benchmarks.Data
+{
+    public class ApplicationDbSeeder
+    {
+        private readonly object _locker = new object();
+        private readonly IRandom _random;
+        private readonly ApplicationDbContext _dbContext;
+        private bool _seeded = false;
+
+        public ApplicationDbSeeder(IRandom random, ApplicationDbContext dbContext)
+        {
+            _random = random;
+            _dbContext = dbContext;
+        }
+
+        public bool Seed()
+        {
+            if (!_seeded)
+            {
+                lock (_locker)
+                {
+                    if (!_seeded)
+                    {
+                        try
+                        {
+                            var world = _dbContext.World.Count();
+                            var fortune = _dbContext.Fortune.Count();
+
+                            if (world == 0 || fortune == 0)
+                            {
+                                if (world == 0)
+                                {
+                                    for (int i = 0; i < 10000; i++)
+                                    {
+                                        _dbContext.World.Add(new World { RandomNumber = _random.Next(1, 10001) });
+                                    }
+                                    _dbContext.SaveChanges();
+                                }
+
+                                if (fortune == 0)
+                                {
+                                    _dbContext.Fortune.Add(new Fortune { Message = "fortune: No such file or directory" });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "A computer scientist is someone who fixes things that aren't broken." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "After enough decimal places, nobody gives a damn." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1" });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "A computer program does what you tell it to do, not what you want it to do." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen" });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "Any program that runs right is obsolete." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "A list is only as strong as its weakest link. — Donald Knuth" });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "Feature: A bug with seniority." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "Computers make very fast, very accurate mistakes." });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "<script>alert(\"This should not be displayed in a browser alert box.\");</script>" });
+                                    _dbContext.Fortune.Add(new Fortune { Message = "フレームワークのベンチマーク" });
+
+                                    _dbContext.SaveChanges();
+                                }
+
+                                Console.WriteLine("Database successfully seeded!");
+                            }
+                            else
+                            {
+                                Console.WriteLine("Database already seeded!");
+                            }
+
+                            _seeded = true;
+                            return true;
+                        }
+                        catch (Exception ex)
+                        {
+                            Console.Error.WriteLine("Error trying to seed the database. Have you run 'dnx ef database update'?");
+                            Console.Error.WriteLine(ex);
+
+                            return false;
+                        }
+                    }
+                }
+            }
+
+            Console.WriteLine("Database already seeded!");
+            return true;
+        }
+    }
+}

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

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

+ 52 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.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.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+
+namespace Benchmarks.Data
+{
+    public class EfDb
+    {
+        private readonly IRandom _random;
+        private readonly ApplicationDbContext _dbContext;
+
+        public EfDb(IRandom random, ApplicationDbContext dbContext)
+        {
+            _random = random;
+            _dbContext = dbContext;
+            _dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
+        }
+
+        public Task<World> LoadSingleQueryRow()
+        {
+            var id = _random.Next(1, 10001);
+            
+            return _dbContext.World.FirstAsync(w => w.Id == id);
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                var id = _random.Next(1, 10001);
+                result[i] = await _dbContext.World.FirstAsync(w => w.Id == id);
+            }
+
+            return result;
+        }
+
+        public async Task<IEnumerable<Fortune>> LoadFortunesRows()
+        {
+            var result = await _dbContext.Fortune.ToListAsync();
+
+            result.Add(new Fortune { Message = "Additional fortune added at request time." });
+            result.Sort();
+
+            return result;
+        }
+    }
+}

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

@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Benchmarks.Data
+{
+    public class Fortune : IComparable<Fortune>, IComparable
+    {
+        public int Id { get; set; }
+
+        [StringLength(2048)]
+        public string Message { get; set; }
+        
+        public int CompareTo(object obj)
+        {
+            var other = obj as Fortune;
+
+            if (other == null)
+            {
+                throw new ArgumentException($"Object to compare must be of type {nameof(Fortune)}", nameof(obj));
+            }
+
+            return CompareTo(other);
+        }
+
+        public int CompareTo(Fortune other)
+        {
+            return Message.CompareTo(other.Message);
+        }
+    }
+}

+ 10 - 0
frameworks/CSharp/aspnetcore/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);
+    }
+}

+ 17 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.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;
+
+namespace Benchmarks.Data
+{
+    public class DefaultRandom : IRandom
+    {
+        private readonly Random _random = new Random();
+
+        public int Next(int minValue, int maxValue)
+        {
+            return _random.Next(minValue, maxValue);
+        }
+    }
+}

+ 122 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.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.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace Benchmarks.Data
+{
+    public class RawDb
+    {
+        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())
+            using (var cmd = db.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);
+
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
+                {
+                    await rdr.ReadAsync();
+
+                    return new World
+                    {
+                        Id = rdr.GetInt32(0),
+                        RandomNumber = rdr.GetInt32(1)
+                    };
+                }
+            }
+        }
+
+        public async Task<World[]> LoadMultipleQueriesRows(int count)
+        {
+            var result = new World[count];
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            using (var cmd = db.CreateCommand())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                cmd.CommandText = "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id";
+                var id = cmd.CreateParameter();
+                id.ParameterName = "@Id";
+                id.DbType = DbType.Int32;
+                cmd.Parameters.Add(id);
+
+                for (int i = 0; i < count; i++)
+                {
+                    id.Value = _random.Next(1, 10001);
+                    using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+                    {
+                        await rdr.ReadAsync();
+
+                        result[i] = new World
+                        {
+                            Id = rdr.GetInt32(0),
+                            RandomNumber = rdr.GetInt32(1)
+                        };
+                    }
+                }
+
+                db.Close();
+            }
+
+            return result;
+        }
+
+        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;
+        }
+    }
+}

+ 12 - 0
frameworks/CSharp/aspnetcore/Benchmarks/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 Benchmarks.Data
+{
+    public class World
+    {
+        public int Id { get; set; }
+
+        public int RandomNumber { get; set; }
+    }
+}

+ 90 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/DebugInfoPageMiddleware.cs

@@ -0,0 +1,90 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Linq;
+using System.Runtime;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.Extensions.PlatformAbstractions;
+
+namespace Benchmarks.Middleware
+{
+    public class DebugInfoPageMiddleware
+    {
+#if DEBUG
+        private static readonly string _configurationName = "Debug";
+#elif RELEASE
+        private static readonly string _configurationName = "Release";
+#else
+        private static readonly string _configurationName = "";
+#endif
+
+        private readonly IHostingEnvironment _hostingEnv;
+        private readonly RequestDelegate _next;
+        private readonly Scenarios _scenarios;
+        private readonly IServerAddressesFeature _serverAddresses;
+
+        public DebugInfoPageMiddleware(RequestDelegate next, IServerAddressesFeature serverAddresses, IHostingEnvironment hostingEnv, Scenarios scenarios)
+        {
+            _next = next;
+            _hostingEnv = hostingEnv;
+            _scenarios = scenarios;
+            _serverAddresses = serverAddresses;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            httpContext.Response.ContentType = "text/html";
+
+            await httpContext.Response.WriteAsync("<!DOCTYPE html><html><head><style>body{font-family:\"Segoe UI\",Arial,Helvetica,Sans-serif};h1,h2,h3{font-family:\"Segoe UI Light\"}</style></head><body>");
+            await httpContext.Response.WriteAsync("<h1>ASP.NET Core Benchmarks</h1>");
+            await httpContext.Response.WriteAsync("<h2>Configuration Information</h2>");
+            await httpContext.Response.WriteAsync("<ul>");
+            await httpContext.Response.WriteAsync($"<li>Environment: {_hostingEnv.EnvironmentName}</li>");
+            await httpContext.Response.WriteAsync($"<li>Framework: {PlatformServices.Default.Application.RuntimeFramework.FullName}</li>");
+            await httpContext.Response.WriteAsync($"<li>Server GC enabled: {GCSettings.IsServerGC}</li>");
+            await httpContext.Response.WriteAsync($"<li>Configuration: {_configurationName}</li>");
+            await httpContext.Response.WriteAsync($"<li>Server: {Program.Server}</li>");
+            await httpContext.Response.WriteAsync($"<li>Server URLs: {string.Join(", ", _serverAddresses.Addresses)}</li>");
+            await httpContext.Response.WriteAsync($"<li>Supports Send File: {httpContext.Features.Get<IHttpSendFileFeature>() != null}</li>");
+
+            await httpContext.Response.WriteAsync($"<li>Server features:<ul>");
+            foreach (var feature in httpContext.Features)
+            {
+                await httpContext.Response.WriteAsync($"<li>{feature.Key.Name}</li>");
+            }
+            await httpContext.Response.WriteAsync($"</ul></li>");
+
+            await httpContext.Response.WriteAsync($"<li>Enabled scenarios:<ul>");
+            var enabledScenarios = _scenarios.GetEnabled();
+            var maxNameLength = enabledScenarios.Max(s => s.Name.Length);
+            foreach (var scenario in enabledScenarios)
+            {
+                await httpContext.Response.WriteAsync($"<li>{scenario.Name}<ul>");
+                foreach (var path in scenario.Paths)
+                {
+                    await httpContext.Response.WriteAsync($"<li><a href=\"{path}\">{path}</a></li>");
+                }
+                await httpContext.Response.WriteAsync($"</ul></li>");
+            }
+            await httpContext.Response.WriteAsync($"</ul></li>");
+
+
+            await httpContext.Response.WriteAsync("</ul>");
+            await httpContext.Response.WriteAsync("</body></html>");
+        }
+    }
+
+    public static class DebugInfoPageMiddlewareExtensions
+    {
+        public static IApplicationBuilder RunDebugInfoPage(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<DebugInfoPageMiddleware>(builder.ServerFeatures.Get<IServerAddressesFeature>());
+        }
+    }
+}

+ 47 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs

@@ -0,0 +1,47 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Benchmarks.Middleware
+{
+    public class ErrorHandlerMiddleware
+    {
+        private readonly RequestDelegate _next;
+
+        public ErrorHandlerMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            try
+            {
+                await _next(httpContext);
+            }
+            catch (Exception ex)
+            {
+                if (!httpContext.Response.HasStarted)
+                {
+                    httpContext.Response.Clear();
+                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
+                    httpContext.Response.ContentType = "text/html";
+                }
+                
+                await httpContext.Response.WriteAsync($"<pre style='color:red'>{ex.ToString()}</pre>");
+            }
+        }
+    }
+
+    public static class ErrorHandlerMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<ErrorHandlerMiddleware>();
+        }
+    }
+}

+ 51 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesDapperMiddleware.cs

@@ -0,0 +1,51 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Benchmarks.Middleware
+{
+    public class FortunesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesDapper));
+
+        private readonly RequestDelegate _next;
+        private readonly DapperDb _db;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesDapperMiddleware(RequestDelegate next, DapperDb db, HtmlEncoder htmlEncoder)
+        {
+            _next = next;
+            _db = db;
+            _htmlEncoder = htmlEncoder;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var rows = await _db.LoadFortunesRows();
+
+                await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+    
+    public static class FortunesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseFortunesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<FortunesDapperMiddleware>();
+        }
+    }
+}

+ 51 - 0
frameworks/CSharp/aspnetcore/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;
+
+namespace Benchmarks.Middleware
+{
+    public class FortunesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesEf));
+
+        private readonly RequestDelegate _next;
+        private readonly EfDb _db;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesEfMiddleware(RequestDelegate next, EfDb db, HtmlEncoder htmlEncoder)
+        {
+            _next = next;
+            _db = db;
+            _htmlEncoder = htmlEncoder;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                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/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;
+
+namespace Benchmarks.Middleware
+{
+    public class FortunesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesRaw));
+
+        private readonly RequestDelegate _next;
+        private readonly RawDb _db;
+        private readonly HtmlEncoder _htmlEncoder;
+
+        public FortunesRawMiddleware(RequestDelegate next, RawDb db, HtmlEncoder htmlEncoder)
+        {
+            _next = next;
+            _db = db;
+            _htmlEncoder = htmlEncoder;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                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>();
+        }
+    }
+}

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

@@ -0,0 +1,57 @@
+// 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 Task _done = Task.FromResult(0);
+        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 _done;
+            }
+
+            return _next(httpContext);
+        }
+    }
+    
+    public static class JsonMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseJson(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<JsonMiddleware>();
+        }
+    }
+}

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

@@ -0,0 +1,47 @@
+// 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.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";
+
+            await httpContext.Response.WriteAsync("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
+
+            foreach (var item in model)
+            {
+                await httpContext.Response.WriteAsync(
+                    $"<tr><td>{htmlEncoder.Encode(item.Id.ToString())}</td><td>{htmlEncoder.Encode(item.Message)}</td></tr>");
+            }
+
+            await httpContext.Response.WriteAsync("</table></body></html>");
+        }
+    }
+}

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

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

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs

@@ -0,0 +1,61 @@
+// 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 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;
+        private readonly EfDb _db;
+
+        public MultipleQueriesEfMiddleware(RequestDelegate next, EfDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                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>();
+        }
+    }
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesRawMiddleware.cs

@@ -0,0 +1,61 @@
+// 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 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;
+        private readonly RawDb _db;
+
+        public MultipleQueriesRawMiddleware(RequestDelegate next, RawDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                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>();
+        }
+    }
+}

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

@@ -0,0 +1,48 @@
+// 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))
+            {
+                httpContext.Response.StatusCode = 200;
+                httpContext.Response.ContentType = "text/plain";
+                // HACK: Setting the Content-Length header manually avoids the cost of serializing the int to a string.
+                //       This is instead of: httpContext.Response.ContentLength = _helloWorldPayload.Length;
+                httpContext.Response.Headers["Content-Length"] = "13";
+                return httpContext.Response.Body.WriteAsync(_helloWorldPayload, 0, _helloWorldPayload.Length);
+            }
+
+            return _next(httpContext);
+        }
+    }
+    
+    public static class PlaintextMiddlewareExtensions
+    {
+        public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<PlaintextMiddleware>();
+        }
+    }
+}

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

@@ -0,0 +1,60 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class SingleQueryDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbSingleQueryDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+        private readonly DapperDb _db;
+
+        public SingleQueryDapperMiddleware(RequestDelegate next, DapperDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var row = await _db.LoadSingleQueryRow();
+
+                var result = JsonConvert.SerializeObject(row, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class SingleQueryDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseSingleQueryDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<SingleQueryDapperMiddleware>();
+        }
+    }
+}

+ 59 - 0
frameworks/CSharp/aspnetcore/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 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;
+        private readonly EfDb _db;
+
+        public SingleQueryEfMiddleware(RequestDelegate next, EfDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                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/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 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;
+        private readonly RawDb _db;
+
+        public SingleQueryRawMiddleware(RequestDelegate next, RawDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                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>();
+        }
+    }
+}

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

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

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

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

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

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

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

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

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

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

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

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <packageSources>
+    <clear />
+    <add key="AspNetVNext" value="https://dotnet.myget.org/F/aspnet1/api/v3/index.json" />
+    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
+  </packageSources>
+</configuration>

+ 138 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Program.cs

@@ -0,0 +1,138 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.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 string Server;
+
+        public static void Main(string[] args)
+        {
+            Args = args;
+
+            Console.WriteLine();
+            Console.WriteLine("ASP.NET Core Benchmarks");
+            Console.WriteLine("-----------------------");
+
+            Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
+
+            var config = new ConfigurationBuilder()
+                .AddCommandLine(args)
+                .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+                .AddJsonFile("hosting.json", optional: true)
+                .Build();
+
+            Server = config["server"] ?? "Kestrel";
+
+            var webHostBuilder = new WebHostBuilder()
+                .UseContentRoot(Directory.GetCurrentDirectory())
+                .UseConfiguration(config)
+                .UseStartup<Startup>()
+                .ConfigureServices(services => services
+                    .AddSingleton(new ConsoleArgs(args))
+                    .AddSingleton<IScenariosConfiguration, ConsoleHostScenariosConfiguration>()
+                    .AddSingleton<Scenarios>()
+                );
+
+            if (String.Equals(Server, "Kestrel", StringComparison.OrdinalIgnoreCase))
+            {
+                var threads = GetThreadCount(config);
+                webHostBuilder = webHostBuilder.UseKestrel((options) =>
+                {
+                    if (threads > 0)
+                    {
+                        options.ThreadCount = threads;
+                    }
+                });
+            }
+            else if (String.Equals(Server, "WebListener", StringComparison.OrdinalIgnoreCase))
+            {
+                webHostBuilder = webHostBuilder.UseWebListener();
+            }
+            else
+            {
+                throw new InvalidOperationException($"Unknown server value: {Server}");
+            }
+
+            var webHost = webHostBuilder.Build();
+
+            Console.WriteLine($"Using server {Server}");
+            Console.WriteLine($"Server GC is currently {(GCSettings.IsServerGC ? "ENABLED" : "DISABLED")}");
+
+            var nonInteractiveValue = config["NonInteractive"];
+            if (nonInteractiveValue == null || !bool.Parse(nonInteractiveValue))
+            {
+                StartInteractiveConsoleThread();
+            }
+
+            webHost.Run();
+        }
+
+        private static void StartInteractiveConsoleThread()
+        {
+            // Run the interaction on a separate thread as we don't have Console.KeyAvailable on .NET Core so can't
+            // do a pre-emptive check before we call Console.ReadKey (which blocks, hard)
+
+            var started = new ManualResetEvent(false);
+
+            var interactiveThread = new Thread(() =>
+            {
+                Console.WriteLine("Press 'C' to force GC or any other key to display GC stats");
+                Console.WriteLine();
+
+                started.Set();
+
+                while (true)
+                {
+                    var key = Console.ReadKey(intercept: true);
+
+                    if (key.Key == ConsoleKey.C)
+                    {
+                        Console.WriteLine();
+                        Console.Write("Forcing GC...");
+                        GC.Collect();
+                        GC.WaitForPendingFinalizers();
+                        GC.Collect();
+                        Console.WriteLine(" done!");
+                    }
+                    else
+                    {
+                        Console.WriteLine();
+                        Console.WriteLine($"Allocated: {GetAllocatedMemory()}");
+                        Console.WriteLine($"Gen 0: {GC.CollectionCount(0)}, Gen 1: {GC.CollectionCount(1)}, Gen 2: {GC.CollectionCount(2)}");
+                    }
+                }
+            });
+
+            interactiveThread.IsBackground = true;
+            interactiveThread.Start();
+
+            started.WaitOne();
+        }
+
+        private static string GetAllocatedMemory(bool forceFullCollection = false)
+        {
+            double bytes = GC.GetTotalMemory(forceFullCollection);
+
+            return $"{((bytes / 1024d) / 1024d).ToString("N2")} MB";
+        }
+
+        private static int GetThreadCount(IConfigurationRoot config)
+        {
+            var threadCountValue = config["threadCount"];
+            return threadCountValue == null ? -1 : int.Parse(threadCountValue);
+        }
+    }
+}
+

+ 27 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Properties/launchSettings.json

@@ -0,0 +1,27 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:3653/",
+      "sslPort": 0
+    }
+  },
+  "profiles": {
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "EnableDbTests": "true",
+        "AspNet_Environment": "Development"
+      }
+    },
+    "Benchmarks": {
+      "commandName": "Benchmarks",
+      "launchUrl": "http://localhost:5000/",
+      "environmentVariables": {
+        "AspNet_Environment": "Development"
+      }
+    }
+  }
+}

+ 185 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs

@@ -0,0 +1,185 @@
+// 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.Data.SqlClient;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Benchmarks.Middleware;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.PlatformAbstractions;
+
+namespace Benchmarks
+{
+    public class Startup
+    {
+        public Startup(IHostingEnvironment hostingEnv, Scenarios scenarios)
+        {
+            // Set up configuration sources.
+            var builder = new ConfigurationBuilder()
+                .SetBasePath(PlatformServices.Default.Application.ApplicationBasePath)
+                .AddCommandLine(Program.Args)
+                .AddJsonFile("appsettings.json")
+                .AddJsonFile($"appsettings.{hostingEnv.EnvironmentName}.json", optional: true)
+                .AddEnvironmentVariables();
+
+            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>();
+            services.AddSingleton<ApplicationDbSeeder>();
+            services.AddEntityFrameworkSqlServer()
+                .AddDbContext<ApplicationDbContext>();
+
+            if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
+            {
+                // TODO: Add support for plugging in different DbProviderFactory implementations via configuration
+                services.AddSingleton<DbProviderFactory>(SqlClientFactory.Instance);
+            }
+
+            if (Scenarios.Any("Ef"))
+            {
+                services.AddScoped<EfDb>();
+            }
+
+            if (Scenarios.Any("Raw"))
+            {
+                services.AddScoped<RawDb>();
+            }
+
+            if (Scenarios.Any("Dapper"))
+            {
+                services.AddScoped<DapperDb>();
+            }
+
+            if (Scenarios.Any("Fortunes"))
+            {
+                services.AddWebEncoders();
+            }
+
+            if (Scenarios.Any("Mvc"))
+            {
+                var mvcBuilder = services
+                    .AddMvcCore()
+                    //.AddApplicationPart(typeof(Startup).GetTypeInfo().Assembly)
+                    .AddControllersAsServices();
+
+                if (Scenarios.MvcJson)
+                {
+                    mvcBuilder.AddJsonFormatters();
+                }
+
+                if (Scenarios.MvcViews || Scenarios.Any("MvcDbFortunes"))
+                {
+                    mvcBuilder
+                        .AddViews()
+                        .AddRazorViewEngine();
+                }
+            }
+        }
+
+        public void Configure(IApplicationBuilder app, ApplicationDbSeeder dbSeeder, ApplicationDbContext dbContext)
+        {
+            app.UseErrorHandler();
+
+            if (Scenarios.Plaintext)
+            {
+                app.UsePlainText();
+            }
+
+            if (Scenarios.Json)
+            {
+                app.UseJson();
+            }
+
+            // Single query endpoints
+            if (Scenarios.DbSingleQueryRaw)
+            {
+                app.UseSingleQueryRaw();
+            }
+
+            if (Scenarios.DbSingleQueryDapper)
+            {
+                app.UseSingleQueryDapper();
+            }
+
+            if (Scenarios.DbSingleQueryEf)
+            {
+                app.UseSingleQueryEf();
+            }
+
+            // Multiple query endpoints
+            if (Scenarios.DbMultiQueryRaw)
+            {
+                app.UseMultipleQueriesRaw();
+            }
+
+            if (Scenarios.DbMultiQueryDapper)
+            {
+                app.UseMultipleQueriesDapper();
+            }
+
+            if (Scenarios.DbMultiQueryEf)
+            {
+                app.UseMultipleQueriesEf();
+            }
+
+            // Fortunes endpoints
+            if (Scenarios.DbFortunesRaw)
+            {
+                app.UseFortunesRaw();
+            }
+
+            if (Scenarios.DbFortunesDapper)
+            {
+                app.UseFortunesDapper();
+            }
+
+            if (Scenarios.DbFortunesEf)
+            {
+                app.UseFortunesEf();
+            }
+
+            if (Scenarios.Any("Db"))
+            {
+                dbContext.Database.EnsureCreated();
+
+                if (!dbSeeder.Seed())
+                {
+                    Environment.Exit(1);
+                }
+            }
+
+            if (Scenarios.Any("Mvc"))
+            {
+                app.UseMvc();
+            }
+
+            if (Scenarios.StaticFiles)
+            {
+                app.UseStaticFiles();
+            }
+
+            app.RunDebugInfoPage();
+        }
+    }
+}

+ 12 - 0
frameworks/CSharp/aspnetcore/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/Benchmarks/Views/Home/Index.cshtml

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

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

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

+ 55 - 0
frameworks/CSharp/aspnetcore/Benchmarks/project.json

@@ -0,0 +1,55 @@
+{
+  "version": "1.0.0-*",
+  "compilationOptions": {
+    "emitEntryPoint": true,
+    "preserveCompilationContext": true
+  },
+  "dependencies": {
+    "Dapper": "1.50.0-*",
+    "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-*",
+    "Microsoft.EntityFrameworkCore.Tools": {
+      "type": "build",
+      "version": "1.0.0-*"
+    },
+    "Microsoft.AspNetCore.Mvc": "1.0.0-*",
+    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
+    "Microsoft.AspNetCore.Server.WebListener": "0.1.0-*",
+    "Microsoft.AspNetCore.StaticFiles": "1.0.0-*",
+    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0-*",
+    "Microsoft.Extensions.Configuration.Json": "1.0.0-*",
+    "Microsoft.Extensions.Configuration.CommandLine": "1.0.0-*",
+    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*"
+  },
+  "frameworks": {
+    "netcoreapp1.0": {
+      "imports": [ "portable-net451+win8", "dnxcore50" ],
+      "dependencies": {
+        "Microsoft.NETCore.App": {
+          "version": "1.0.0-*",
+          "type": "platform"
+        },
+        "System.Runtime.Serialization.Primitives": "4.1.0-*"
+      }
+    },
+    "net451": { }
+  },
+  "tools": {
+    "Microsoft.EntityFrameworkCore.Tools": {
+      "version": "1.0.0-*",
+      "imports": [
+        "dnxcore50",
+        "portable-net452+win81"
+      ]
+    }
+  },
+  "content": [
+    "appsettings.json",
+    "wwwroot",
+    "Views"
+  ],
+  "runtimeOptions": {
+    "configProperties": {
+      "System.GC.Server": true
+    }
+  }
+}

BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/128B.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/16KB.txt


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


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/1MB.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/4KB.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/512B.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/512KB.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/5MB.txt


BIN
frameworks/CSharp/aspnetcore/Benchmarks/wwwroot/favicon.ico


+ 32 - 0
frameworks/CSharp/aspnetcore/README.md

@@ -0,0 +1,32 @@
+# ASP.NET Core Tests on Windows and Linux
+
+See [.NET Core](http://dot.net) and [ASP.NET Core](https://github.com/aspnet) for more information.
+
+This includes tests for plaintext and json serialization.
+
+## Infrastructure Software Versions
+
+**Language**
+
+* C# 6.0
+
+**Platforms**
+
+* .NET Core (Windows and Linux)
+
+**Web Servers**
+
+* [Kestrel](https://github.com/aspnet/kestrelHttpServer)
+* WebListener
+
+**Web Stack**
+
+* ASP.NET Core
+* ASP.NET Core MVC
+
+## Paths & Source for Tests
+
+* [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"

+ 119 - 0
frameworks/CSharp/aspnetcore/benchmark_config.json

@@ -0,0 +1,119 @@
+{
+  "framework": "aspnetcore",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "Kestrel",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-linux": {
+      "setup_file": "setup-mvc",
+      "json_url": "/mvc/json",
+      "plaintext_url": "/mvc/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "Kestrel",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "win": {
+      "setup_file": "setup-windows",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "Kestrel",
+      "os": "Windows",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-win",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-win": {
+      "setup_file": "setup-mvc-windows",
+      "json_url": "/mvc/json",
+      "plaintext_url": "/mvc/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "Kestrel",
+      "os": "Windows",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-win",
+      "notes": "",
+      "versus": ""
+    },
+    "weblistener": {
+      "setup_file": "setup-weblistener",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "WebListener",
+      "os": "Windows",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-weblistener",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-weblistener": {
+      "setup_file": "setup-mvc-weblistener",
+      "json_url": "/mvc/json",
+      "plaintext_url": "/mvc/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "webserver": "WebListener",
+      "os": "Windows",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-weblistener",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 8 - 0
frameworks/CSharp/aspnetcore/run-linux.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+fw_depends mono dotnetcore
+sudo apt-get install unzip libunwind8 -y
+
+cd Benchmarks
+dotnet restore
+dotnet build -c Release -f netcoreapp1.0
+dotnet run -c Release server.urls=http://*:8080 scenarios=$1 server=kestrel threadCount=8 NonInteractive=true &

+ 8 - 0
frameworks/CSharp/aspnetcore/run-windows.ps1

@@ -0,0 +1,8 @@
+param([string]$scenarios="[default]", [string]$server="kestrel")
+
+$scenarios = (-split $scenarios) -join ","
+
+cd Benchmarks
+dotnet restore
+dotnet build -c Release -f netcoreapp1.0
+Start-Process -NoNewWindow dotnet "run -c Release server.urls=http://*:8080 server=$server threadCount=8 NonInteractive=true scenarios=$scenarios"

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-mvc-weblistener.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+fw_depends dotnetcore
+powershell run-windows.ps1 -scenarios 'mvcjson,mvcplain' -server weblistener

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-mvc-windows.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+fw_depends dotnetcore
+powershell run-windows.ps1 -scenarios 'mvcjson,mvcplain' -server kestrel

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-mvc.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh mvcjson,mvcplain

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-weblistener.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+fw_depends dotnetcore
+powershell run-windows.ps1 -scenarios [default] -server weblistener

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-windows.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+fw_depends dotnetcore
+powershell run-windows.ps1 -scenarios [default] -server kestrel

+ 3 - 0
frameworks/CSharp/aspnetcore/setup.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh [default]

+ 17 - 0
toolset/setup/linux/languages/dotnetcore.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+
+RETCODE=$(fw_exists ${IROOT}/dotnetcore.installed)
+[ ! "$RETCODE" == 0 ] || { \
+  source $IROOT/dotnetcore.installed
+  dotnet --info
+  return 0; }
+
+sudo apt-get install unzip libunwind8 -y
+fw_get -O https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview2/scripts/obtain/dotnet-install.sh
+chmod +x $IROOT/dotnet-install.sh
+
+$IROOT/dotnet-install.sh
+echo "PATH=$HOME/.dotnet:$PATH" > $IROOT/dotnetcore.installed
+
+source $IROOT/dotnetcore.installed
+dotnet --info