Просмотр исходного кода

Add a benchmark app for Blazor (#13911)

* Add a benchmark app for Blazor
Pranav K 6 лет назад
Родитель
Сommit
fc3b154c9e
53 измененных файлов с 2122 добавлено и 0 удалено
  1. 1 0
      eng/Build.props
  2. 6 0
      src/Components/benchmarkapps/BlazingPizza.Server/App.razor
  3. 16 0
      src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj
  4. 13 0
      src/Components/benchmarkapps/BlazingPizza.Server/JSRuntimeExtensions.cs
  5. 27 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/Address.cs
  6. 27 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/LatLong.cs
  7. 25 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/Order.cs
  8. 42 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/Pizza.cs
  9. 20 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/PizzaSpecial.cs
  10. 11 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/PizzaTopping.cs
  11. 9 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/Point.cs
  12. 13 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/Topping.cs
  13. 9 0
      src/Components/benchmarkapps/BlazingPizza.Server/Model/UserInfo.cs
  14. 68 0
      src/Components/benchmarkapps/BlazingPizza.Server/OrderState.cs
  15. 34 0
      src/Components/benchmarkapps/BlazingPizza.Server/Pages/Checkout.razor
  16. 74 0
      src/Components/benchmarkapps/BlazingPizza.Server/Pages/Index.razor
  17. 33 0
      src/Components/benchmarkapps/BlazingPizza.Server/Pages/Map.razor
  18. 22 0
      src/Components/benchmarkapps/BlazingPizza.Server/Pages/_Host.cshtml
  19. 1 0
      src/Components/benchmarkapps/BlazingPizza.Server/Pages/_Imports.razor
  20. 23 0
      src/Components/benchmarkapps/BlazingPizza.Server/Program.cs
  21. 51 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/AddressEditor.razor
  22. 92 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/ConfigurePizzaDialog.razor
  23. 18 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/ConfiguredPizzaItem.razor
  24. 20 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/MainLayout.razor
  25. 28 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/OrderReview.razor
  26. 13 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/TemplatedDialog.razor
  27. 36 0
      src/Components/benchmarkapps/BlazingPizza.Server/Shared/TemplatedList.razor
  28. 77 0
      src/Components/benchmarkapps/BlazingPizza.Server/SpecialsService.cs
  29. 58 0
      src/Components/benchmarkapps/BlazingPizza.Server/Startup.cs
  30. 124 0
      src/Components/benchmarkapps/BlazingPizza.Server/ToppingsService.cs
  31. 9 0
      src/Components/benchmarkapps/BlazingPizza.Server/_Imports.razor
  32. 9 0
      src/Components/benchmarkapps/BlazingPizza.Server/appsettings.Development.json
  33. 8 0
      src/Components/benchmarkapps/BlazingPizza.Server/appsettings.json
  34. 5 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/bootstrap/bootstrap.min.css
  35. 0 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/bootstrap/bootstrap.min.css.map
  36. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-300.woff
  37. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-300.woff2
  38. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-500.woff
  39. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-500.woff2
  40. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-700.woff
  41. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-700.woff2
  42. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-regular.woff
  43. BIN
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-regular.woff2
  44. 40 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand.css
  45. 736 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/site.css
  46. 77 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/bike.svg
  47. 46 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/logo.svg
  48. 106 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/pizza-slice.svg
  49. 72 0
      src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/user.svg
  50. 2 0
      src/Components/benchmarkapps/Directory.Build.props
  51. 3 0
      src/Components/benchmarkapps/Directory.Build.targets
  52. 12 0
      src/Components/benchmarkapps/NuGet.config
  53. 6 0
      src/Components/benchmarkapps/README.md

+ 1 - 0
eng/Build.props

@@ -42,6 +42,7 @@
 
     <!-- Exclude the benchmarks because they use <PackageReference>. -->
     <ProjectToExclude Include="
+                      $(RepoRoot)src\Components\benchmarkapps\**\*.csproj;
                       $(RepoRoot)src\Mvc\benchmarkapps\**\*.csproj;
                       $(RepoRoot)src\Servers\Kestrel\perf\PlatformBenchmarks\**\*.csproj;
                       $(RepoRoot)src\SignalR\perf\benchmarkapps\**\*.csproj;

+ 6 - 0
src/Components/benchmarkapps/BlazingPizza.Server/App.razor

@@ -0,0 +1,6 @@
+<Router AppAssembly="typeof(Program).Assembly">
+    <NotFound>Page not found</NotFound>
+    <Found Context="routeData">
+        <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)"></RouteView>
+    </Found>
+</Router>

+ 16 - 0
src/Components/benchmarkapps/BlazingPizza.Server/BlazingPizza.Server.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.0</TargetFramework>
+    <TargetFramework Condition="'$(BenchmarksTargetFramework)' != ''">$(BenchmarksTargetFramework)</TargetFramework>
+    <IsTestAssetProject>true</IsTestAssetProject>
+  </PropertyGroup>
+
+  <!-- These references are used when building on the Benchmarks Server. -->
+  <ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
+
+    <FrameworkReference Update="Microsoft.AspNetCore.App" RuntimeFrameworkVersion="$(MicrosoftAspNetCoreAppPackageVersion)" />
+    <FrameworkReference Update="Microsoft.NETCore.App" RuntimeFrameworkVersion="$(MicrosoftNETCoreAppPackageVersion)" />
+  </ItemGroup>
+
+</Project>

+ 13 - 0
src/Components/benchmarkapps/BlazingPizza.Server/JSRuntimeExtensions.cs

@@ -0,0 +1,13 @@
+using Microsoft.JSInterop;
+using System.Threading.Tasks;
+
+namespace BlazingPizza.Server
+{
+    public static class JSRuntimeExtensions
+    {
+        public static ValueTask<bool> Confirm(this IJSRuntime jsRuntime, string message)
+        {
+            return jsRuntime.InvokeAsync<bool>("confirm", message);
+        }
+    }
+}

+ 27 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/Address.cs

@@ -0,0 +1,27 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BlazingPizza
+{
+    public class Address
+    {
+        public int Id { get; set; }
+
+        [Required, MaxLength(100)]
+        public string Name { get; set; }
+
+        [Required, MaxLength(100)]
+        public string Line1 { get; set; }
+
+        [MaxLength(100)]
+        public string Line2 { get; set; }
+
+        [Required, MaxLength(50)]
+        public string City { get; set; }
+
+        [Required, MaxLength(20)]
+        public string Region { get; set; }
+
+        [Required, MaxLength(20)]
+        public string PostalCode { get; set; }
+    }
+}

+ 27 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/LatLong.cs

@@ -0,0 +1,27 @@
+namespace BlazingPizza
+{
+    public class LatLong
+    {
+        public LatLong()
+        {
+        }
+
+        public LatLong(double latitude, double longitude) : this()
+        {
+            Latitude = latitude;
+            Longitude = longitude;
+        }
+
+        public double Latitude { get; set; }
+
+        public double Longitude { get; set; }
+
+        public static LatLong Interpolate(LatLong start, LatLong end, double proportion)
+        {
+            // The Earth is flat, right? So no need for spherical interpolation.
+            return new LatLong(
+                start.Latitude + (end.Latitude - start.Latitude) * proportion,
+                start.Longitude + (end.Longitude - start.Longitude) * proportion);
+        }
+    }
+}

+ 25 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/Order.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BlazingPizza
+{
+    public class Order
+    {
+        public int OrderId { get; set; }
+
+        public string UserId { get; set; }
+
+        public DateTime CreatedTime { get; set; }
+
+        public Address DeliveryAddress { get; set; } = new Address();
+
+        public LatLong DeliveryLocation { get; set; }
+
+        public List<Pizza> Pizzas { get; set; } = new List<Pizza>();
+
+        public decimal GetTotalPrice() => Pizzas.Sum(p => p.GetTotalPrice());
+
+        public string GetFormattedTotalPrice() => GetTotalPrice().ToString("0.00");
+    }
+}

+ 42 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/Pizza.cs

@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BlazingPizza
+{
+    /// <summary>
+    /// Represents a customized pizza as part of an order
+    /// </summary>
+    public class Pizza
+    {
+        public const int DefaultSize = 12;
+        public const int MinimumSize = 9;
+        public const int MaximumSize = 17;
+
+        public int Id { get; set; }
+
+        public int OrderId { get; set; }
+
+        public PizzaSpecial Special { get; set; }
+
+        public int SpecialId { get; set; }
+
+        public int Size { get; set; }
+
+        public List<PizzaTopping> Toppings { get; set; }
+
+        public decimal GetBasePrice()
+        {
+            return ((decimal)Size / (decimal)DefaultSize) * Special.BasePrice;
+        }
+
+        public decimal GetTotalPrice()
+        {
+            return GetBasePrice() + Toppings.Sum(t => t.Topping.Price);
+        }
+
+        public string GetFormattedTotalPrice()
+        {
+            return GetTotalPrice().ToString("0.00");
+        }
+    }
+}

+ 20 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/PizzaSpecial.cs

@@ -0,0 +1,20 @@
+namespace BlazingPizza
+{
+    /// <summary>
+    /// Represents a pre-configured template for a pizza a user can order
+    /// </summary>
+    public class PizzaSpecial
+    {
+        public int Id { get; set; }
+
+        public string Name { get; set; }
+
+        public int BasePrice { get; set; }
+
+        public string Description { get; set; }
+
+        public string ImageUrl { get; set; }
+
+        public string GetFormattedBasePrice() => BasePrice.ToString("0.00");
+    }
+}

+ 11 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/PizzaTopping.cs

@@ -0,0 +1,11 @@
+namespace BlazingPizza
+{
+    public class PizzaTopping
+    {
+        public Topping Topping { get; set; }
+
+        public int ToppingId { get; set; }
+        
+        public int PizzaId { get; set; }
+    }
+}

+ 9 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/Point.cs

@@ -0,0 +1,9 @@
+namespace BlazingPizza.Server
+{
+    public class Point
+    {
+        public double X { get; set; }
+
+        public double Y { get; set; }
+    }
+}

+ 13 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/Topping.cs

@@ -0,0 +1,13 @@
+namespace BlazingPizza
+{
+    public class Topping
+    {
+        public int Id { get; set; }
+
+        public string Name { get; set; }
+
+        public decimal Price { get; set; }
+
+        public string GetFormattedPrice() => Price.ToString("0.00");
+    }
+}

+ 9 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Model/UserInfo.cs

@@ -0,0 +1,9 @@
+namespace BlazingPizza
+{
+    public class UserInfo
+    {
+        public bool IsAuthenticated { get; set; }
+
+        public string Name { get; set; }
+    }
+}

+ 68 - 0
src/Components/benchmarkapps/BlazingPizza.Server/OrderState.cs

@@ -0,0 +1,68 @@
+using System.Collections.Generic;
+using Microsoft.Extensions.Logging;
+
+namespace BlazingPizza.Server
+{
+    public class OrderState
+    {
+        private readonly ILogger<OrderState> logger;
+
+        public OrderState(ILogger<OrderState> logger)
+        {
+            this.logger = logger;
+        }
+
+        public bool ShowingConfigureDialog { get; private set; }
+
+        public Pizza ConfiguringPizza { get; private set; }
+
+        public Order Order { get; private set; } = new Order();
+
+        public void ShowConfigurePizzaDialog(PizzaSpecial special)
+        {
+            logger.LogInformation($"Configuring pizza {special.Id}");
+
+            ConfiguringPizza = new Pizza()
+            {
+                Special = special,
+                SpecialId = special.Id,
+                Size = Pizza.DefaultSize,
+                Toppings = new List<PizzaTopping>(),
+            };
+
+            ShowingConfigureDialog = true;
+        }
+
+        public void CancelConfigurePizzaDialog()
+        {
+            ConfiguringPizza = null;
+
+            ShowingConfigureDialog = false;
+        }
+
+        public void ConfirmConfigurePizzaDialog()
+        {
+            logger.LogInformation($"Adding pizza {ConfiguringPizza.SpecialId}");
+
+            Order.Pizzas.Add(ConfiguringPizza);
+            ConfiguringPizza = null;
+
+            ShowingConfigureDialog = false;
+        }
+
+        public void RemoveConfiguredPizza(Pizza pizza)
+        {
+            Order.Pizzas.Remove(pizza);
+        }
+
+        public void ResetOrder()
+        {
+            Order = new Order();
+        }
+
+        public void ReplaceOrder(Order order)
+        {
+            Order = order;
+        }
+    }
+}

+ 34 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Pages/Checkout.razor

@@ -0,0 +1,34 @@
+@page "/checkout"
+@inject OrderState OrderState
+
+<div class="main">
+    <EditForm Model="@OrderState.Order.DeliveryAddress">
+        <div class="checkout-cols">
+            <div class="checkout-order-details">
+                <h4>Review order</h4>
+                <OrderReview Order="@OrderState.Order" />
+            </div>
+
+            <div class="checkout-delivery-address">
+                <h4>Deliver to...</h4>
+                <AddressEditor Address="@OrderState.Order.DeliveryAddress" />
+            </div>
+        </div>
+
+        <button type="submit" class="checkout-button btn btn-warning">
+            Place order
+        </button>
+
+        <DataAnnotationsValidator />
+    </EditForm>
+</div>
+
+@functions {
+    protected override void OnInitialized()
+    {
+        // Try to recover any temporary saved order
+        OrderState.ReplaceOrder(new Order
+        {
+        });
+    }
+}

+ 74 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Pages/Index.razor

@@ -0,0 +1,74 @@
+@page "/"
+@inject OrderState OrderState
+@inject IJSRuntime JS
+@inject SpecialsService Specials
+
+<div class="main">
+    <ul class="pizza-cards">
+        @if (specials != null)
+        {
+            @foreach (var special in specials)
+            {
+                var id = $"pizza{special.Id}";
+                <li id="@id" @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))">
+                    <div class="pizza-info">
+                        <span class="title">@special.Name</span>
+                        @special.Description
+                        <span class="price">@special.GetFormattedBasePrice()</span>
+                    </div>
+                </li>
+            }
+        }
+    </ul>
+</div>
+
+<div class="sidebar" id="pizzaOrders" pizzaCount="@Order.Pizzas.Count">
+    @if (Order.Pizzas.Any())
+    {
+        <div class="order-contents">
+            <h2>Your order</h2>
+
+            @foreach (var configuredPizza in Order.Pizzas)
+            {
+                <ConfiguredPizzaItem Pizza="configuredPizza" OnRemoved="@(() => RemovePizza(configuredPizza))" />
+            }
+        </div>
+    }
+    else
+    {
+        <div class="empty-cart">Choose a pizza<br>to get started</div>
+    }
+
+    <div class="order-total @(Order.Pizzas.Any() ? "" : "hidden")">
+        Total:
+        <span class="total-price">@Order.GetFormattedTotalPrice()</span>
+        <a href="checkout" class="btn btn-warning" disabled="@(Order.Pizzas.Count == 0)">
+            Order >
+        </a>
+    </div>
+</div>
+
+<TemplatedDialog Show="OrderState.ShowingConfigureDialog">
+    <ConfigurePizzaDialog
+        Pizza="OrderState.ConfiguringPizza"
+        OnCancel="OrderState.CancelConfigurePizzaDialog"
+        OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />
+</TemplatedDialog>
+
+@functions {
+    IList<PizzaSpecial> specials;
+    Order Order => OrderState.Order;
+
+    protected override void OnInitialized()
+    {
+        specials = Specials.GetSpecials();
+    }
+
+    async Task RemovePizza(Pizza configuredPizza)
+    {
+        if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?"))
+        {
+            OrderState.RemoveConfiguredPizza(configuredPizza);
+        }
+    }
+}

+ 33 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Pages/Map.razor

@@ -0,0 +1,33 @@
+@page "/orderstatus"
+@using System.Threading
+@using Microsoft.JSInterop
+@inject IJSRuntime JSRuntime
+@implements IDisposable
+
+
+<div id="status" data-loc="@locString">Your order is on it's way. Here is it's current location: (@locX, @locY)</div>
+
+@functions {
+    private int locX;
+    private int locY;
+    private Random random = new Random();
+    private Timer timer;
+    private string locString;
+
+    protected override void OnInitialized()
+    {
+        timer = new Timer(_ =>
+        {
+            locX = random.Next(1000);
+            locY = random.Next(1000);
+            locString = $"{locX},{locY}";
+
+            InvokeAsync(() => StateHasChanged());
+        }, default, TimeSpan.Zero, TimeSpan.FromSeconds(2));
+    }
+
+    public void Dispose()
+    {
+        timer.Dispose();
+    }
+}

+ 22 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Pages/_Host.cshtml

@@ -0,0 +1,22 @@
+@page "/"
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>BlazorServerSIde</title>
+    <base href="~/" />
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
+            asp-fallback-href="css/bootstrap/bootstrap.min.css"
+            asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
+            crossorigin="anonymous"
+            integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"/>
+    <link href="css/site.css" rel="stylesheet" />
+</head>
+<body>
+    <app>@(await Html.RenderComponentAsync<BlazingPizza.Server.App>(RenderMode.ServerPrerendered))</app>
+
+     <script src="_framework/blazor.server.js"></script>
+</body>
+</html>

+ 1 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Pages/_Imports.razor

@@ -0,0 +1 @@
+@layout MainLayout

+ 23 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Program.cs

@@ -0,0 +1,23 @@
+using System;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace BlazingPizza.Server
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            CreateHostBuilder(args).Build().Run();
+        }
+
+        public static IHostBuilder CreateHostBuilder(string[] args) =>
+            Host.CreateDefaultBuilder(args)
+                .ConfigureWebHostDefaults(webBuilder =>
+                {
+                    webBuilder.UseStartup<Startup>();
+                });
+    }
+}

+ 51 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/AddressEditor.razor

@@ -0,0 +1,51 @@
+<div class="form-field">
+    <label>Name:</label>
+    <div>
+        <InputText @bind-Value="@Address.Name" />
+        <ValidationMessage For="@(() => Address.Name)" />
+    </div>
+</div>
+
+<div class="form-field">
+    <label>Line 1:</label>
+    <div>
+        <InputText @bind-Value="@Address.Line1" id="Line1" />
+        <ValidationMessage For="@(() => Address.Line1)" />
+    </div>
+</div>
+
+<div class="form-field">
+    <label>Line 2:</label>
+    <div>
+        <InputText @bind-Value="@Address.Line2" />
+        <ValidationMessage For="@(() => Address.Line2)" />
+    </div>
+</div>
+
+<div class="form-field">
+    <label>City:</label>
+    <div>
+        <InputText @bind-Value="@Address.City" />
+        <ValidationMessage For="@(() => Address.City)" />
+    </div>
+</div>
+
+<div class="form-field">
+    <label>Region:</label>
+    <div>
+        <InputText @bind-Value="@Address.Region" />
+        <ValidationMessage For="@(() => Address.Region)" />
+    </div>
+</div>
+
+<div class="form-field">
+    <label>Postal code:</label>
+    <div>
+        <InputText @bind-Value="@Address.PostalCode" />
+        <ValidationMessage For="@(() => Address.PostalCode)" />
+    </div>
+</div>
+
+@functions {
+    [Parameter] public Address Address { get; set; }
+}

+ 92 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/ConfigurePizzaDialog.razor

@@ -0,0 +1,92 @@
+@inject ToppingsService ToppingsService
+
+<div class="dialog-title">
+    <h2>@Pizza.Special.Name</h2>
+    @Pizza.Special.Description
+</div>
+<form class="dialog-body">
+    <div>
+        <label>Size:</label>
+        <input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" @bind-value="@Pizza.Size" @bind-value:event="oninput" />
+        <span class="size-label">
+            @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice()))
+        </span>
+    </div>
+    <div>
+        <label>Extra Toppings:</label>
+        @if (toppings == null)
+        {
+            <select class="custom-select" disabled>
+                <option>(loading...)</option>
+            </select>
+        }
+        else if (Pizza.Toppings.Count >= 6)
+        {
+            <div>(maximum reached)</div>
+        }
+        else
+        {
+            <select id="custom-select" class="custom-select" @onchange="@ToppingSelected">
+                <option value="-1" disabled selected>(select)</option>
+                @for (var i = 0; i < toppings.Count; i++)
+                {
+                    <option value="@i">@toppings[i].Name - (£@(toppings[i].GetFormattedPrice()))</option>
+                }
+            </select>
+        }
+    </div>
+
+    <div class="toppings">
+        @foreach (var topping in Pizza.Toppings)
+        {
+            <div class="topping">
+                @topping.Topping.Name
+                <span class="topping-price">@topping.Topping.GetFormattedPrice()</span>
+                <button type="button" class="delete-topping" @onclick="@(() => RemoveTopping(topping.Topping))">x</button>
+            </div>
+        }
+    </div>
+</form>
+
+<div class="dialog-buttons">
+    <button class="btn btn-secondary mr-auto" @onclick="@OnCancel" id="cancel">Cancel</button>
+    <span class="mr-center">
+        Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
+    </span>
+    <button class="btn btn-success ml-auto" @onclick="@OnConfirm" id="confirm">Order ></button>
+</div>
+
+@functions {
+    IList<Topping> toppings;
+
+    [Parameter] public Pizza Pizza { get; set; }
+    [Parameter] public EventCallback OnCancel { get; set; }
+    [Parameter] public EventCallback OnConfirm { get; set; }
+
+    protected override void OnInitialized()
+    {
+        toppings = ToppingsService.GetToppings();
+    }
+
+    void ToppingSelected(ChangeEventArgs e)
+    {
+        if (int.TryParse((string)e.Value, out var index) && index >= 0)
+        {
+            AddTopping(toppings[index]);
+        }
+    }
+
+    void AddTopping(Topping topping)
+    {
+        if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null)
+        {
+            Pizza.Toppings.Add(new PizzaTopping() { Topping = topping });
+        }
+    }
+
+    void RemoveTopping(Topping topping)
+    {
+        Pizza.Toppings.RemoveAll(pt => pt.Topping == topping);
+        StateHasChanged();
+    }
+}

+ 18 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/ConfiguredPizzaItem.razor

@@ -0,0 +1,18 @@
+<div class="cart-item">
+    <a @onclick="@OnRemoved" class="delete-item">x</a>
+    <div class="title">@(Pizza.Size)" @Pizza.Special.Name</div>
+    <ul>
+        @foreach (var topping in Pizza.Toppings)
+        {
+            <li>+ @topping.Topping.Name</li>
+        }
+    </ul>
+    <div class="item-price">
+        @Pizza.GetFormattedTotalPrice()
+    </div>
+</div>
+
+@functions {
+    [Parameter] public Pizza Pizza { get; set; }
+    [Parameter] public EventCallback OnRemoved { get; set; }
+}

+ 20 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/MainLayout.razor

@@ -0,0 +1,20 @@
+@inherits LayoutComponentBase
+
+<div class="top-bar">
+    <img class="logo" src="img/logo.svg" />
+            
+    <NavLink href="" class="nav-tab" Match="NavLinkMatch.All">
+        <img src="img/pizza-slice.svg" />
+        <div>Get Pizza</div>
+    </NavLink>
+
+    <NavLink href="myorders" class="nav-tab">
+        <img src="img/bike.svg" />
+        <div>My Orders</div>
+    </NavLink>
+</div>
+
+<div class="content">
+    @Body
+</div>
+

+ 28 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/OrderReview.razor

@@ -0,0 +1,28 @@
+@foreach (var pizza in Order.Pizzas)
+{
+    <p>
+        <strong>
+            @(pizza.Size)"
+            @pizza.Special.Name
+            (£@pizza.GetFormattedTotalPrice())
+        </strong>
+    </p>
+
+    <ul>
+        @foreach (var topping in pizza.Toppings)
+        {
+            <li>+ @topping.Topping.Name</li>
+        }
+    </ul>
+}
+
+<p>
+    <strong>
+        Total price:
+        £@Order.GetFormattedTotalPrice()
+    </strong>
+</p>
+
+@functions {
+    [Parameter] public Order Order { get; set; }
+}

+ 13 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/TemplatedDialog.razor

@@ -0,0 +1,13 @@
+@if (Show)
+{
+    <div class="dialog-container">
+        <div class="dialog">
+            @ChildContent
+        </div>
+    </div>
+}
+
+@functions {
+    [Parameter] public RenderFragment ChildContent { get; set; }
+    [Parameter] public bool Show { get; set; }
+}

+ 36 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Shared/TemplatedList.razor

@@ -0,0 +1,36 @@
+@typeparam TItem
+
+@if (items == null)
+{
+    @LoadingContent
+}
+else if (items.Count == 0)
+{
+    @EmptyContent
+}
+else
+{
+    <div class="list-group @ListGroupClass">
+        @foreach (var item in items)
+        {
+            <div class="list-group-item">
+                @ItemContent(item)
+            </div>
+        }
+    </div>
+}
+
+@functions {
+    List<TItem> items;
+
+    [Parameter] public Func<Task<List<TItem>>> Loader { get; set; }
+    [Parameter] public RenderFragment LoadingContent { get; set; }
+    [Parameter] public RenderFragment EmptyContent { get; set; }
+    [Parameter] public RenderFragment<TItem> ItemContent { get; set; }
+    [Parameter] public string ListGroupClass { get; set; }
+
+    protected override async Task OnParametersSetAsync()
+    {
+        items = await Loader();
+    }
+}

+ 77 - 0
src/Components/benchmarkapps/BlazingPizza.Server/SpecialsService.cs

@@ -0,0 +1,77 @@
+using System.Collections.Generic;
+
+namespace BlazingPizza.Server
+{
+    public class SpecialsService
+    {
+        public IList<PizzaSpecial> GetSpecials()
+        {
+            return new PizzaSpecial[]
+            {
+                new PizzaSpecial()
+                {
+                    Name = "Basic Cheese Pizza",
+                    Description = "It's cheesy and delicious. Why wouldn't you want one?",
+                    BasePrice = 9,
+                    ImageUrl = "img/pizzas/cheese.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 2,
+                    Name = "The Baconatorizor",
+                    Description = "It has EVERY kind of bacon",
+                    BasePrice = 12,
+                    ImageUrl = "img/pizzas/bacon.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 3,
+                    Name = "Classic pepperoni",
+                    Description = "It's the pizza you grew up with, but Blazing hot!",
+                    BasePrice = 10,
+                    ImageUrl = "img/pizzas/pepperoni.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 4,
+                    Name = "Buffalo chicken",
+                    Description = "Spicy chicken, hot sauce and bleu cheese, guaranteed to warm you up",
+                    BasePrice = 12,
+                    ImageUrl = "img/pizzas/meaty.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 5,
+                    Name = "Mushroom Lovers",
+                    Description = "It has mushrooms. Isn't that obvious?",
+                    BasePrice = 11,
+                    ImageUrl = "img/pizzas/mushroom.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 6,
+                    Name = "The Brit",
+                    Description = "When in London...",
+                    BasePrice = 10,
+                    ImageUrl = "img/pizzas/brit.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 7,
+                    Name = "Veggie Delight",
+                    Description = "It's like salad, but on a pizza",
+                    BasePrice = 11,
+                    ImageUrl = "img/pizzas/salad.jpg",
+                },
+                new PizzaSpecial()
+                {
+                    Id = 8,
+                    Name = "Margherita",
+                    Description = "Traditional Italian pizza with tomatoes and basil",
+                    BasePrice = 9,
+                    ImageUrl = "img/pizzas/margherita.jpg",
+                },
+            };
+        }
+    }
+}

+ 58 - 0
src/Components/benchmarkapps/BlazingPizza.Server/Startup.cs

@@ -0,0 +1,58 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.ResponseCompression;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System.Linq;
+using System.Net.Mime;
+
+namespace BlazingPizza.Server
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        public IConfiguration Configuration { get; }
+
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddMvc();
+
+            services.AddServerSideBlazor(options => options.DetailedErrors = true);
+
+            services.AddScoped<OrderState>();
+            services.AddScoped<SpecialsService>();
+            services.AddScoped<ToppingsService>();
+
+            services.AddResponseCompression(options =>
+            {
+                options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
+                    new[] { MediaTypeNames.Application.Octet });
+            });
+        }
+
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            app.UseResponseCompression();
+
+            if (env.IsDevelopment())
+            {
+                app.UseDeveloperExceptionPage();
+            }
+
+            app.UseStaticFiles();
+
+            app.UseRouting();
+
+            app.UseEndpoints(endpoints =>
+            {
+                endpoints.MapBlazorHub();
+                endpoints.MapFallbackToPage("/_Host");
+            });
+        }
+    }
+}

+ 124 - 0
src/Components/benchmarkapps/BlazingPizza.Server/ToppingsService.cs

@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+
+namespace BlazingPizza.Server
+{
+    public class ToppingsService
+    {
+        public IList<Topping> GetToppings()
+        {
+            return new Topping[]
+            {
+                new Topping()
+                {
+                    Name = "Extra cheese",
+                    Price = 2.50m,
+                },
+                new Topping()
+                {
+                    Name = "American bacon",
+                    Price = 2.99m,
+                },
+                new Topping()
+                {
+                    Name = "British bacon",
+                    Price = 2.99m,
+                },
+                new Topping()
+                {
+                    Name = "Canadian bacon",
+                    Price = 2.99m,
+                },
+                new Topping()
+                {
+                    Name = "Tea and crumpets",
+                    Price = 5.00m
+                },
+                new Topping()
+                {
+                    Name = "Fresh-baked scones",
+                    Price = 4.50m,
+                },
+                new Topping()
+                {
+                    Name = "Bell peppers",
+                    Price = 1.00m,
+                },
+                new Topping()
+                {
+                    Name = "Onions",
+                    Price = 1.00m,
+                },
+                new Topping()
+                {
+                    Name = "Mushrooms",
+                    Price = 1.00m,
+                },
+                new Topping()
+                {
+                    Name = "Pepperoni",
+                    Price = 1.00m,
+                },
+                new Topping()
+                {
+                    Name = "Duck sausage",
+                    Price = 3.20m,
+                },
+                new Topping()
+                {
+                    Name = "Venison meatballs",
+                    Price = 2.50m,
+                },
+                new Topping()
+                {
+                    Name = "Served on a silver platter",
+                    Price = 250.99m,
+                },
+                new Topping()
+                {
+                    Name = "Lobster on top",
+                    Price = 64.50m,
+                },
+                new Topping()
+                {
+                    Name = "Sturgeon caviar",
+                    Price = 101.75m,
+                },
+                new Topping()
+                {
+                    Name = "Artichoke hearts",
+                    Price = 3.40m,
+                },
+                new Topping()
+                {
+                    Name = "Fresh tomatos",
+                    Price = 1.50m,
+                },
+                new Topping()
+                {
+                    Name = "Basil",
+                    Price = 1.50m,
+                },
+                new Topping()
+                {
+                    Name = "Steak (medium-rare)",
+                    Price = 8.50m,
+                },
+                new Topping()
+                {
+                    Name = "Blazing hot peppers",
+                    Price = 4.20m,
+                },
+                new Topping()
+                {
+                    Name = "Buffalo chicken",
+                    Price = 5.00m,
+                },
+                new Topping()
+                {
+                    Name = "Blue cheese",
+                    Price = 2.50m,
+                },
+            };
+        }
+    }
+}

+ 9 - 0
src/Components/benchmarkapps/BlazingPizza.Server/_Imports.razor

@@ -0,0 +1,9 @@
+@using System.Net.Http
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Components.Forms
+@using Microsoft.AspNetCore.Components.Routing
+@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.JSInterop
+@using BlazingPizza.Server
+@using BlazingPizza.Server.Shared
+@using System.Security.Claims

+ 9 - 0
src/Components/benchmarkapps/BlazingPizza.Server/appsettings.Development.json

@@ -0,0 +1,9 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Debug",
+      "System": "Information",
+      "Microsoft": "Information"
+    }
+  }
+}

+ 8 - 0
src/Components/benchmarkapps/BlazingPizza.Server/appsettings.json

@@ -0,0 +1,8 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 5 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/bootstrap/bootstrap.min.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/bootstrap/bootstrap.min.css.map


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-300.woff


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-300.woff2


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-500.woff


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-500.woff2


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-700.woff


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-700.woff2


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-regular.woff


BIN
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-regular.woff2


+ 40 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand.css

@@ -0,0 +1,40 @@
+/* quicksand-300 - latin */
+@font-face {
+    font-family: 'Quicksand';
+    font-style: normal;
+    font-weight: 300;
+    src: local('Quicksand Light'), local('Quicksand-Light'),
+         url('quicksand-v8-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+         url('quicksand-v8-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+  }
+  
+  /* quicksand-regular - latin */
+  @font-face {
+    font-family: 'Quicksand';
+    font-style: normal;
+    font-weight: 400;
+    src: local('Quicksand Regular'), local('Quicksand-Regular'),
+         url('quicksand-v8-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+         url('quicksand-v8-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+  }
+  
+  /* quicksand-500 - latin */
+  @font-face {
+    font-family: 'Quicksand';
+    font-style: normal;
+    font-weight: 500;
+    src: local('Quicksand Medium'), local('Quicksand-Medium'),
+         url('quicksand-v8-latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+         url('quicksand-v8-latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+  }
+  
+  /* quicksand-700 - latin */
+  @font-face {
+    font-family: 'Quicksand';
+    font-style: normal;
+    font-weight: 700;
+    src: local('Quicksand Bold'), local('Quicksand-Bold'),
+         url('quicksand-v8-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
+         url('quicksand-v8-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+  }
+  

+ 736 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/site.css

@@ -0,0 +1,736 @@
+@import url('font/quicksand.css');
+
+body, html {
+    height: 100%;
+}
+
+body {
+    padding-top: 5rem;
+    flex-direction: column;
+    font-family: 'quicksand';
+    overflow-y: hidden;
+}
+
+form {
+    width: 100%;
+}
+
+.form-group.row > .col-form-label {
+    text-align: right;
+}
+
+.top-bar {
+    height: 5rem;
+    background-color: rgb(192,0,0);
+    background-image: linear-gradient(rgba(0,0,0,0.3) 0%, rgba(0,0,0,0) 70%);
+    display: flex;
+    align-items: stretch;
+    color: white;
+    box-shadow: 0 1px 3px rgba(0,0,0,0.4);
+    padding: 0 3rem;
+    position: fixed;
+    top: 0;
+    right: 0;
+    left: 0;
+    z-index: 1030;
+}
+
+.logo {
+    margin-right: 3rem;
+    width: 9rem;
+}
+
+.content {
+    display: flex;
+    height: 100%;
+    z-index: 1;
+    background-color: white;
+}
+
+.main {
+    flex-grow: 1;
+    overflow-y: auto;
+    background: linear-gradient(rgba(0,0,0,0) 40%, rgba(0,0,0,0.4) 80%);
+    padding: 1.5rem !important;
+}
+
+.nav-tab {
+    margin: 0;
+    padding: 0.3rem 1.8rem;
+    display: inline-block;
+    background-color: rgba(0,0,0,0.1);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    font-size: 0.9rem;
+    color: white;
+    position: relative;
+    text-transform: uppercase;
+    transition: 0.2s ease-out;
+}
+
+    .nav-tab:hover {
+        color: white;
+        text-decoration: none;
+        background-color: rgba(255,255,255,0.3);
+    }
+
+    .nav-tab.active {
+        background-color: rgba(255,255,255,0.2);
+        color: #fff2cc;
+    }
+
+    .nav-tab img {
+        height: 2rem;
+        margin-bottom: 0.25rem;
+    }
+
+    .nav-tab.active img {
+        filter: brightness(0) saturate(100%) invert(93%) sepia(18%) saturate(797%) hue-rotate(316deg) brightness(109%) contrast(101%);
+    }
+
+    .nav-tab.active:after {
+        content: "";
+        position: absolute;
+        bottom: -1rem;
+        z-index: 1;
+        width: 0px;
+        height: 0px;
+        border-left: 0.6rem solid transparent;
+        border-right: 0.6rem solid transparent;
+        border-top: 1rem solid rgb(205,51,51);
+    }
+
+.user-info {
+    margin-left: auto;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+}
+
+    .user-info img {
+        margin-right: 0.7rem;
+        width: 2.6rem;
+    }
+
+    .user-info .username {
+        display: block;
+        font-weight: 700;
+        line-height: 0.7rem;
+        margin-top: 0.5rem;
+    }
+
+    .user-info a {
+        color: #fff2cc;
+        font-size: 0.8rem;
+    }
+
+.pizza-cards {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, 20rem);
+    grid-gap: 2rem;
+    justify-content: center;
+    padding-left: 0;
+}
+
+    .pizza-cards > li {
+        height: 10rem;
+        position: relative;
+        background-size: cover;
+        border-radius: 0.5rem;
+        list-style-type: none;
+        box-shadow: 0 3px 4px rgba(0,0,0,0.4);
+        transition: 0.1s ease-out;
+    }
+
+        .pizza-cards > li:hover {
+            transform: scale(1.02);
+        }
+
+.pizza-info {
+    border-radius: 0.5rem;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    position: absolute;
+    background: linear-gradient(rgba(0,0,0,0.7) 30%, rgba(0,0,0,0) 80%);
+    padding: 1rem 1rem;
+    color: #fff2cc;
+    cursor: pointer;
+    text-shadow: 0 2px 2px rgba(0,0,0,0.5);
+    line-height: 1.25rem;
+}
+
+    .pizza-info .title {
+        color: white;
+        font-size: 1.4rem;
+        display: block;
+        margin: 0.2rem 0 0.4rem 0;
+        font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+        text-transform: uppercase;
+    }
+
+    .pizza-info .price {
+        position: absolute;
+        bottom: 0.5rem;
+        right: 1rem;
+        font-size: 1.5rem;
+        font-weight: 700;
+        padding: 0rem 0.7rem;
+        border-radius: 4px;
+        background-color: #08af08;
+        color: white;
+        line-height: 2rem;
+    }
+
+.price::before {
+    content: '£';
+    font-weight: 300;
+    font-size: 1.2rem;
+    margin-right: 0.2rem;
+}
+
+.sidebar {
+    background-color: #2b2b2b;
+    width: 20rem;
+    display: flex;
+    flex-direction: column;
+    color: white;
+}
+
+.order-contents {
+    overflow-y: auto;
+    padding: 2rem 1.5rem 1.5rem 1.5rem;
+    flex-grow: 1;
+}
+
+    .order-contents h2 {
+        color: #fff2cc;
+        font-size: 1.3rem;
+        font-weight: 300;
+        margin-bottom: 1rem;
+        font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+        text-transform: uppercase;
+    }
+
+.order-total {
+    background-color: rgb(191, 52, 52);
+    height: 4rem;
+    flex-shrink: 0;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    color: white;
+    font-size: 1.2rem;
+    transition: all 600ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
+    padding: 0 1.5rem;
+}
+
+.order-total.hidden {
+    transform: translate3d(0, 4rem, 0);
+}
+
+    .order-total .total-price {
+        font-weight: 700;
+        font-size: 1.5rem;
+    }
+
+        .order-total .total-price::before {
+            content: '£';
+            font-weight: 300;
+            margin: 0 0.1rem 0 0.4rem;
+        }
+
+    .order-total .btn {
+        margin-left: auto;
+        font-weight: 700;
+        border-radius: 20px;
+        padding: 0.4rem 1.2rem;
+    }
+
+.checkout-button {
+    margin: auto;
+    display: block;
+    font-weight: 700;
+    border-radius: 20px;
+    padding: 0.4rem 1.2rem;
+}
+
+.cart-item {
+    background-color: #333333;
+    padding: 0.8rem 1.2rem;
+    border-radius: 6px;
+    font-weight: 100;
+    margin-top: 1rem;
+    position: relative;
+}
+
+    .cart-item .title {
+        font-weight: 700;
+    }
+
+    .cart-item ul {
+        padding: 0;
+        margin: 0.4rem 0.6rem;
+    }
+
+    .cart-item li {
+        list-style-type: none;
+        margin-left: 0rem;
+        font-size: 0.8rem;
+    }
+
+.empty-cart {
+    text-align: center;
+    margin: auto;
+    font-size: 1.5rem;
+    font-weight: 100;
+    color: #676767;
+}
+
+.item-price {
+    font-weight: 500;
+}
+
+    .item-price::before {
+        content: '£';
+        font-weight: 100;
+        margin-right: 0.3rem;
+    }
+
+.delete-item {
+    position: absolute;
+    top: 0;
+    right: 0;
+    content: 'X';
+    cursor: pointer;
+    color: #fff2cc;
+    width: 2rem;
+    height: 2rem;
+    text-align: center;
+}
+
+    .delete-item:hover {
+        text-decoration: none;
+        color: #fff2cc;
+        background-color: rgba(255,255,255,0.1);
+    }
+
+.configured-pizza-item {
+    display: flex;
+    flex-direction: row;
+}
+
+.dialog-container {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background-color: rgba(0,0,0,0.5);
+    z-index: 2000;
+    display: flex;
+    animation: dialog-container-entry 0.2s;
+}
+
+@keyframes dialog-container-entry {
+    0% {
+        opacity: 0;
+    }
+
+    100% {
+        opacity: 1;
+    }
+}
+
+.dialog {
+    background-color: white;
+    box-shadow: 0 0 12px rgba(0,0,0,0.6);
+    display: flex;
+    flex-direction: column;
+    z-index: 2000;
+    align-self: center;
+    margin: auto;
+    width: 700px;
+    max-height: calc(100% - 3rem);
+    animation: dialog-entry 0.4s;
+    animation-timing-function: cubic-bezier(0.075, 0.820, 0.165, 1.000);
+}
+
+@keyframes dialog-entry {
+    0% {
+        transform: translateY(30px) scale(0.95);
+    }
+
+    100% {
+        transform: translateX(0px) scale(1.0);
+    }
+}
+
+.dialog-title {
+    background-color: #444;
+    color: #fff2cc;
+    padding: 1.3rem 2rem;
+}
+
+    .dialog-title h2 {
+        color: white;
+        font-size: 1.4rem;
+        margin: 0;
+        font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+        text-transform: uppercase;
+        line-height: 1.3rem;
+    }
+
+.dialog-body {
+    flex-grow: 1;
+    padding: 0.5rem 3rem 1rem 0;
+}
+
+.dialog-buttons {
+    height: 4rem;
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    background-color: #eee;
+    padding: 0 1rem;
+}
+
+    .dialog-body > div {
+        display: flex;
+        margin-top: 1rem;
+        align-items: center;
+    }
+
+    .dialog-body label {
+        text-align: right;
+        width: 200px;
+        margin: 0 1.5rem;
+    }
+
+    .dialog-body input, .dialog-body select {
+        flex-grow: 1;
+        width: unset;
+    }
+
+    .dialog-body .size-label {
+        min-width: 110px;
+        text-align: right;
+    }
+
+.dialog .toppings {
+    text-align: center;
+    display: block;
+    padding-left: 4rem;
+}
+
+.dialog .topping {
+    display: inline-block;
+    background-color: #a04343;
+    color: white;
+    padding: 0.2rem 1rem;
+    border-radius: 2rem;
+    margin: 0.4rem 0.3rem;
+    font-weight: 700;
+}
+
+.dialog .topping-price {
+    font-weight: 100;
+    font-size: 0.8rem;
+}
+
+.dialog .topping-price::before {
+    content: '£';
+}
+
+.delete-topping {
+    background: none;
+    border: none;
+    color: white;
+    padding: 0.2rem 0.2rem 0.3rem 0.2rem;
+    cursor: pointer;
+}
+
+.delete-topping:hover {
+    color: yellow;
+}
+
+.form-message {
+    padding: 0.5rem;
+    font-weight: 700;
+}
+
+.dialog .price {
+    font-weight: 700;
+    font-size: 1.5rem;
+}
+
+.orders-list .list-group-item {
+    display: flex;
+}
+
+.orders-list .col {
+    margin: auto;
+}
+
+.orders-list h5 {
+    color: #c03939;
+    font-size: 1.3rem;
+    font-weight: 300;
+    margin: 0.2rem 0 0 0;
+    font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+    text-transform: uppercase;
+}
+
+.track-order {
+    background-color: white;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.4);
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+}
+
+    .track-order > div {
+        overflow-y: hidden;
+    }
+
+.track-order-title {
+    background-color: #eee;
+    display: flex;
+    align-items: center;
+    padding: 1rem 3rem;
+}
+
+    .track-order-title h2 {
+        color: #c03939;
+        font-size: 1.3rem;
+        font-weight: 300;
+        margin: 0rem;
+        font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+        text-transform: uppercase;
+    }
+
+.track-order-body {
+    flex-grow: 1;
+    display: flex;
+}
+
+.track-order-details {
+    overflow-y: auto;
+    padding: 1.5rem 3rem;
+    flex-grow: 1;
+}
+
+.track-order-map {
+    width: 350px;
+    flex-shrink: 0;
+}
+
+a.sign-in {
+    background: none;
+    border: 1.5px solid white;
+    border-radius: 0.7em;
+    color: white;
+    text-transform: uppercase;
+    padding: 0.2rem 0.8rem 0.1rem 0.8rem;
+    font-family: 'Bahnschrift', Arial, Helvetica, sans-serif;
+    font-weight: 100;
+    cursor: pointer;
+    transition: 0.2s ease-out;
+}
+
+    a.sign-in:hover {
+        background-color: rgba(255,255,255,0.3);
+        color: #fff2cc;
+        border-color: #fff2cc;
+    }
+
+.user-info a.sign-out {
+    color: #fff2cc;
+    font-size: 0.8rem;
+    cursor: pointer;
+}
+
+    .user-info a.sign-out:hover {
+        text-decoration: underline;
+    }
+
+input[type=range] {
+    -webkit-appearance: none;
+    margin: 7.1px 0;
+    height: 21px;
+}
+
+    input[type=range]:focus {
+        outline: none;
+    }
+
+    input[type=range]::-webkit-slider-runnable-track {
+        width: 100%;
+        height: 5.8px;
+        cursor: pointer;
+        box-shadow: 0px 0px 1px #000000, 0px 0px 0px #0d0d0d;
+        background: #dcdcdc;
+        border-radius: 1.3px;
+        border: 0px solid #010101;
+    }
+
+    input[type=range]::-webkit-slider-thumb {
+        box-shadow: 0.9px 0.9px 1px rgba(0, 0, 49, 0.43), 0px 0px 0.9px rgba(0, 0, 75, 0.43);
+        border: 0px solid #00001e;
+        height: 20px;
+        width: 20px;
+        border-radius: 10px;
+        background: #d45352;
+        cursor: pointer;
+        -webkit-appearance: none;
+        margin-top: -7.1px;
+    }
+
+    input[type=range]:focus::-webkit-slider-runnable-track {
+        background: #e1e1e1;
+    }
+
+    input[type=range]::-moz-range-track {
+        width: 100%;
+        height: 5.8px;
+        cursor: pointer;
+        box-shadow: 0px 0px 1px #000000, 0px 0px 0px #0d0d0d;
+        background: #dcdcdc;
+        border-radius: 1.3px;
+        border: 0px solid #010101;
+    }
+
+    input[type=range]::-moz-range-thumb {
+        box-shadow: 0.9px 0.9px 1px rgba(0, 0, 49, 0.43), 0px 0px 0.9px rgba(0, 0, 75, 0.43);
+        border: 0px solid #00001e;
+        height: 20px;
+        width: 20px;
+        border-radius: 10px;
+        background: #d45352;
+        cursor: pointer;
+    }
+
+    input[type=range]::-ms-track {
+        width: 100%;
+        height: 5.8px;
+        cursor: pointer;
+        background: transparent;
+        border-color: transparent;
+        color: transparent;
+    }
+
+    input[type=range]::-ms-fill-lower {
+        background: #d7d7d7;
+        border: 0px solid #010101;
+        border-radius: 2.6px;
+        box-shadow: 0px 0px 1px #000000, 0px 0px 0px #0d0d0d;
+    }
+
+    input[type=range]::-ms-fill-upper {
+        background: #dcdcdc;
+        border: 0px solid #010101;
+        border-radius: 2.6px;
+        box-shadow: 0px 0px 1px #000000, 0px 0px 0px #0d0d0d;
+    }
+
+    input[type=range]::-ms-thumb {
+        box-shadow: 0.9px 0.9px 1px rgba(0, 0, 49, 0.43), 0px 0px 0.9px rgba(0, 0, 75, 0.43);
+        border: 0px solid #00001e;
+        height: 20px;
+        width: 20px;
+        border-radius: 10px;
+        background: #d45352;
+        cursor: pointer;
+        margin-top: 0;
+    }
+
+.checkout-cols {
+    display: flex;
+}
+    .checkout-cols h4 {
+        margin-bottom: 1.5rem;
+    }
+
+    .checkout-cols > div {
+        flex: 1;
+        margin: 1rem;
+        border: 1px solid #ddd;
+        background: rgba(255,255,255,0.3);
+        padding: 1.25rem 1rem;
+    }
+
+        .checkout-cols > div:first-child {
+            margin-left: 0;
+        }
+
+        .checkout-cols > div:last-child {
+            margin-right: 0;
+        }
+
+.loading-bar {
+    position: absolute;
+    top: calc(50% - 3px);
+    left: calc(50% - 250px);
+    width: 500px;
+    height: 6px;
+    background-color: white;
+    overflow: hidden;
+    box-shadow: 0 1px 3px rgba(0,0,0,0.2)
+}
+
+    .loading-bar::after {
+        content: '';
+        display: block;
+        width: 200px;
+        height: 100%;
+        background-color: #dc105a;
+        animation: progressbar-slide 1s infinite;
+        animation-timing-function: ease-in-out;
+    }
+
+.form-field {
+    display: flex;
+    margin: 0.5rem;
+}
+
+    .form-field > label {
+        width: 8rem;
+    }
+
+    .form-field > div {
+        flex-grow: 1;
+    }
+
+    .form-field input {
+        width: 100%;
+    }
+
+.valid.modified:not([type=checkbox]) {
+    outline: 1px solid #26b050;
+}
+
+.invalid {
+    outline: 1px solid red;
+}
+
+.validation-message {
+    color: red;
+}
+
+@keyframes progressbar-slide {
+    0% {
+        transform: translateX(-200px);
+    }
+
+    70% {
+        transform: translateX(500px);
+    }
+
+    100% {
+        transform: translateX(500px);
+    }
+}

+ 77 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/bike.svg

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="14.566216mm"
+   height="12.915195mm"
+   viewBox="0 0 14.566216 12.915195"
+   version="1.1"
+   id="svg6773"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="bike.svg">
+  <defs
+     id="defs6767" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="-495.3304"
+     inkscape:cy="-464.16476"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3018"
+     inkscape:window-height="1744"
+     inkscape:window-x="665"
+     inkscape:window-y="3070"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata6770">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-14.639502,-13.107878)">
+    <path
+       d="m 24.422045,14.741241 c 0,0.80786 -0.65263,1.4605 -1.4605,1.4605 -0.80786,0 -1.46402,-0.65264 -1.46402,-1.4605 0,-0.80786 0.65616,-1.46403 1.46402,-1.46403 0.80787,0 1.4605,0.65617 1.4605,1.46403 z"
+       style="fill:none;stroke:#ffffff;stroke-width:0.33866665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+       id="path4889"
+       inkscape:connector-curvature="0" />
+    <path
+       d="m 19.426715,23.546573 c 0,1.273528 -1.03364,2.307167 -2.30717,2.307167 -1.27352,0 -2.30716,-1.033639 -2.30716,-2.307167 0,-1.273528 1.03364,-2.307166 2.30716,-2.307166 1.27353,0 2.30717,1.033638 2.30717,2.307166 z"
+       style="fill:none;stroke:#ffffff;stroke-width:0.33866665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+       id="path4891"
+       inkscape:connector-curvature="0" />
+    <path
+       d="m 29.036385,23.546573 c 0,1.273528 -1.03364,2.307167 -2.30717,2.307167 -1.27353,0 -2.30717,-1.033639 -2.30717,-2.307167 0,-1.273528 1.03364,-2.307166 2.30717,-2.307166 1.27353,0 2.30717,1.033638 2.30717,2.307166 z"
+       style="fill:none;stroke:#ffffff;stroke-width:0.33866665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+       id="path4893"
+       inkscape:connector-curvature="0" />
+    <path
+       d="m 22.587605,19.711881 -1.04422,-0.10231 0.98072,-1.70039 2.23661,1.21709 c -0.0212,0.0212 -0.0212,0.0423 -0.0423,0.0635 l -1.7533,0.98425 v -0.0423 c 0,-0.20814 -0.16934,-0.39864 -0.37748,-0.4198 z m 4.13808,0.88194 c 0.39864,0 0.77611,0.0847 1.10772,0.22931 0.21167,-0.37747 0.35631,-0.81845 0.35631,-1.27706 0,-1.38289 -1.12889,-2.5153 -2.50825,-2.5153 -0.2928,0 -0.56444,0.0635 -0.83608,0.14816 0.10583,0.20814 0.1658,0.43745 0.18697,0.69145 l -2.82222,-1.55222 c -0.0423,-0.0212 -0.0811,-0.0423 -0.12347,-0.0423 -0.48331,-0.18697 -1.02659,-0.0212 -1.29823,0.44097 l -1.46402,2.51531 c -0.0176,0.0423 -0.0388,0.10231 -0.06,0.14464 l -4.45558,-0.45861 v 0.83608 l 3.13619,0.96308 c 1.21356,0.35631 2.09198,1.488727 2.09198,2.808115 0,0.296334 -0.0423,0.567973 -0.127,0.839612 h 4.01461 c -0.0811,-0.271639 -0.12347,-0.543278 -0.12347,-0.839612 0,-1.612194 1.31586,-2.931585 2.92452,-2.931585 z"
+       style="fill:none;stroke:#ffffff;stroke-width:0.33866665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+       id="path4895"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

Разница между файлами не показана из-за своего большого размера
+ 46 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/logo.svg


+ 106 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/pizza-slice.svg

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="12.864264mm"
+   height="12.890507mm"
+   viewBox="0 0 12.864264 12.890507"
+   version="1.1"
+   id="svg7666"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="pizza-slice.svg">
+  <defs
+     id="defs7660">
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4885">
+      <path
+         d="M -3.9725e-5,540 H 960 V 1.2207e-4 L -4.3448e-5,6.1035e-5"
+         id="path4883"
+         inkscape:connector-curvature="0"
+         style="clip-rule:evenodd" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4875">
+      <path
+         d="M -1.9395e-5,540 H 960 V 1.2207e-4 L -2.3118e-5,6.1035e-5"
+         id="path4873"
+         inkscape:connector-curvature="0"
+         style="clip-rule:evenodd" />
+    </clipPath>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="-538.54671"
+     inkscape:cy="-372.78284"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3018"
+     inkscape:window-height="1744"
+     inkscape:window-x="665"
+     inkscape:window-y="3070"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7663">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-26.073816,-37.310701)">
+    <g
+       id="g4869"
+       transform="matrix(0.35277777,0,0,-0.35277777,-82.464891,223.70045)"
+       style="stroke:#ffffff;stroke-opacity:1">
+      <g
+         id="g4871"
+         clip-path="url(#clipPath4875)"
+         style="stroke:#ffffff;stroke-opacity:1">
+        <path
+           d="m 331.92,516.06 c 0,0 -0.03,0.03 0,0 -0.03,0.03 -0.03,0.03 -0.03,0.03 -3.14,3.13 -6.77,5.57 -10.84,7.23 0,0 0,0 0,0 l -12.47,-30.6 30.61,12.47 c 0,0 0,0 0,0 -1.7,4.1 -4.13,7.73 -7.27,10.87 z m -10.3,-1.7 c -1.13,1.13 -1.13,2.93 -0.03,4.03 1.1,1.1 2.9,1.1 4.03,-0.03 1.13,-1.14 1.13,-2.94 0.03,-4.04 -1.1,-1.1 -2.9,-1.1 -4.03,0.04 z m -8.27,-14.94 2.3,5.63 c 0.5,-0.16 0.97,-0.36 1.37,-0.76 1.2,-1.2 1.2,-3.14 0,-4.34 -1,-1 -2.5,-1.16 -3.67,-0.53 z m 12.14,5.07 c -1.14,1.13 -1.14,2.93 -0.04,4.03 1.1,1.1 2.9,1.1 4.04,-0.03 1.13,-1.14 1.13,-2.94 0.03,-4.04 -1.1,-1.1 -2.9,-1.1 -4.03,0.04 z"
+           style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+           id="path4877"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+    <g
+       id="g4879"
+       transform="matrix(0.35277777,0,0,-0.35277777,-82.464891,223.70045)"
+       style="stroke:#ffffff;stroke-opacity:1">
+      <g
+         id="g4881"
+         clip-path="url(#clipPath4885)"
+         style="stroke:#ffffff;stroke-opacity:1">
+        <path
+           d="m 343.52,506.99 c -1.93,4.67 -4.73,8.87 -8.3,12.43 -3.6,3.6 -7.77,6.37 -12.37,8.31 -0.73,0.33 -1.56,-0.04 -1.83,-0.77 -0.27,-0.73 0.07,-1.53 0.8,-1.87 4.23,-1.76 8.07,-4.33 11.4,-7.67 3.34,-3.33 5.9,-7.16 7.7,-11.5 0.07,-0.2 0.2,-0.33 0.34,-0.47 0.4,-0.4 1,-0.53 1.53,-0.33 0.7,0.3 1.03,1.17 0.73,1.87 z"
+           style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+           id="path4887"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+  </g>
+</svg>

+ 72 - 0
src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/img/user.svg

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="15.113004mm"
+   height="15.113004mm"
+   viewBox="0 0 15.113004 15.113004"
+   version="1.1"
+   id="svg7709"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="user.svg">
+  <defs
+     id="defs7703" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="-578.58286"
+     inkscape:cy="28.560012"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3018"
+     inkscape:window-height="1744"
+     inkscape:window-x="665"
+     inkscape:window-y="3070"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7706">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-98.276833,-141.27683)">
+    <path
+       d="m 98.806,148.83333 c 0,-3.88055 3.14678,-7.02733 7.02733,-7.02733 3.88056,0 7.02734,3.14678 7.02734,7.02733 0,3.88056 -3.14678,7.02734 -7.02734,7.02734 -3.88055,0 -7.02733,-3.14678 -7.02733,-7.02734 z"
+       style="fill:none;stroke:#ffffff;stroke-width:1.05833328;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
+       id="path4913"
+       inkscape:connector-curvature="0" />
+    <path
+       d="m 108.23222,147.28111 c 0,1.2947 -1.04775,2.34245 -2.34244,2.34245 -1.29117,0 -2.33892,-1.04775 -2.33892,-2.34245 0,-1.29117 1.04775,-2.33892 2.33892,-2.33892 1.29469,0 2.34244,1.04775 2.34244,2.33892 z"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.35277775"
+       id="path4915"
+       inkscape:connector-curvature="0" />
+    <path
+       d="m 110.51117,153.92392 0.0847,-1.66864 c 0,-0.35278 -0.17639,-0.70203 -0.46919,-0.93839 -0.64206,-0.52564 -1.4605,-0.87842 -2.27895,-1.11125 -0.58561,-0.17639 -1.22766,-0.29281 -1.92969,-0.29281 -0.64206,0 -1.28764,0.11642 -1.92969,0.29281 -0.81845,0.23283 -1.63689,0.64558 -2.27895,1.11125 -0.2928,0.23636 -0.46919,0.58561 -0.46919,0.93839 v 1.73214 c 1.46755,0.40569 0.98777,1.64747 4.70605,1.54869 4.36034,-0.25753 2.94217,-1.18533 4.56495,-1.61219 z"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.35277775"
+       id="path4917"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 2 - 0
src/Components/benchmarkapps/Directory.Build.props

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

+ 3 - 0
src/Components/benchmarkapps/Directory.Build.targets

@@ -0,0 +1,3 @@
+<!-- This file prevents any other Directory.Build.targets from a parent folder to be loaded -->
+<Project>
+</Project>

+ 12 - 0
src/Components/benchmarkapps/NuGet.config

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <packageSources>
+        <clear />
+        <add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
+        <add key="extensions" value="https://dotnetfeed.blob.core.windows.net/aspnet-extensions/index.json" />
+        <add key="entityframeworkcore" value="https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json" />
+        <add key="aspnetcore-tooling" value="https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json" />
+        <add key="aspnetcore" value="https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore/index.json" />
+        <add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
+    </packageSources>
+</configuration>

+ 6 - 0
src/Components/benchmarkapps/README.md

@@ -0,0 +1,6 @@
+## Blazor benchmarks
+
+These projects assist in Benchmarking Components.
+See https://github.com/aspnet/Benchmarks#benchmarks for usage guidance on using the Benchmarking tool with your application
+
+

Некоторые файлы не были показаны из-за большого количества измененных файлов