* Add benchmarks:
- Add new benchmark project
- Add sample app that can be benchmarked
- Check ratio and report failures with exit code.
This commit is contained in:
Chris Sienkiewicz 2022-12-06 14:35:55 -08:00 коммит произвёл GitHub
Родитель b412228ed6
Коммит 13ad9d5c10
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
141 изменённых файлов: 1969 добавлений и 4 удалений

Просмотреть файл

@ -182,6 +182,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compiler Tests", "Compiler
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Common.Test", "src\Razor\test\Microsoft.AspNetCore.Razor.Common.Test\Microsoft.AspNetCore.Razor.Common.Test.csproj", "{23E48E5E-91FC-421E-B122-C7D084FCE39A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Microbenchmarks.Generator", "src\Compiler\perf\Microsoft.AspNetCore.Razor.Microbenchmarks.Generator\Microsoft.AspNetCore.Razor.Microbenchmarks.Generator.csproj", "{7400A168-2552-49C7-93E3-D4DAA90C216F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -766,6 +768,14 @@ Global
{23E48E5E-91FC-421E-B122-C7D084FCE39A}.Release|Any CPU.Build.0 = Release|Any CPU
{23E48E5E-91FC-421E-B122-C7D084FCE39A}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{23E48E5E-91FC-421E-B122-C7D084FCE39A}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.Release|Any CPU.Build.0 = Release|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{7400A168-2552-49C7-93E3-D4DAA90C216F}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -850,6 +860,7 @@ Global
{97DE8703-467C-49A7-BCE4-42FF1FEC8AC2} = {FB7C870E-A173-4F75-BE63-4EF39C79A759}
{A9F9B5E5-C5C2-4860-BE56-038C70ADBAC9} = {FB7C870E-A173-4F75-BE63-4EF39C79A759}
{23E48E5E-91FC-421E-B122-C7D084FCE39A} = {92463391-81BE-462B-AC3C-78C6C760741F}
{7400A168-2552-49C7-93E3-D4DAA90C216F} = {C2C98051-0F39-47F2-80B6-E72B29159F2C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}

Просмотреть файл

@ -70,7 +70,7 @@
Versions below this comment are not managed by automation and can be changed as needed.
-->
<PropertyGroup>
<PropertyGroup Label="Manual">
<!-- Several packages from the editor are used for testing HTML support, and share the following version. -->
<Tooling_HtmlEditorPackageVersion>17.5.101-preview-0002</Tooling_HtmlEditorPackageVersion>
<!-- Several packages share the MS.CA.Testing version -->
@ -81,8 +81,6 @@
<VisualStudioLanguageServerProtocolVersion>17.4.1008-preview</VisualStudioLanguageServerProtocolVersion>
<MicrosoftNetCompilersToolsetVersion>4.4.0</MicrosoftNetCompilersToolsetVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>$(RoslynPackageVersion)</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
</PropertyGroup>
<PropertyGroup Label="Manual">
<!-- dotnet/runtime packages -->
<MicrosoftExtensionsPackageVersion>6.0.0</MicrosoftExtensionsPackageVersion>
<SystemCollectionsImmutablePackageVersion>6.0.0</SystemCollectionsImmutablePackageVersion>
@ -164,12 +162,14 @@
<BenchmarkDotNetVersion>0.13.0.1555</BenchmarkDotNetVersion>
<DiffPlexVersion>1.5.0</DiffPlexVersion>
<FluentAssertionsVersion>6.7.0</FluentAssertionsVersion>
<MicrosoftBuildLocatorVersion>1.4.1</MicrosoftBuildLocatorVersion>
<MicrosoftBuildVersion>17.3.0-preview-22364-05</MicrosoftBuildVersion>
<MicrosoftBuildFrameworkVersion>$(MicrosoftBuildVersion)</MicrosoftBuildFrameworkVersion>
<MicrosoftBuildUtilitiesCoreVersion>$(MicrosoftBuildVersion)</MicrosoftBuildUtilitiesCoreVersion>
<MicrosoftCodeAnalysisCommonVersion>4.0.0-4.final</MicrosoftCodeAnalysisCommonVersion>
<MicrosoftCodeAnalysisCSharpVersion>4.0.0-4.final</MicrosoftCodeAnalysisCSharpVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.3.0-2.final</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildVersion>4.3.0-2.final</MicrosoftCodeAnalysisWorkspacesMSBuildVersion>
<MicrosoftCSharpVersion>4.7.0</MicrosoftCSharpVersion>
<MicrosoftCssParserVersion>1.0.0-20200708.1</MicrosoftCssParserVersion>
<MicrosoftExtensionsDependencyModelVersion>6.0.0</MicrosoftExtensionsDependencyModelVersion>
@ -182,5 +182,7 @@
<SystemTextJsonVersion>6.0.0</SystemTextJsonVersion>
<XunitAssertVersion>$(XunitVersion)</XunitAssertVersion>
<XunitExtensibilityExecutionVersion>$(XunitVersion)</XunitExtensibilityExecutionVersion>
<!-- Benchmarks -->
<Benchmarks_BaselineSourceGeneratorsVersion>7.0.0-preview.5.22528.1</Benchmarks_BaselineSourceGeneratorsVersion>
</PropertyGroup>
</Project>

Просмотреть файл

@ -4,14 +4,17 @@
<PackageVersion Include="DiffPlex" Version="$(DiffPlexVersion)" />
<PackageVersion Include="FluentAssertions" Version="$(FluentAssertionsVersion)" />
<PackageVersion Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkVersion)" />
<PackageVersion Include="Microsoft.Build.Locator" Version="$(MicrosoftBuildLocatorVersion)" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCoreVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="$(MicrosoftCodeAnalysisCommonVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisCSharpWorkspacesVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="$(MicrosoftCodeAnalysisWorkspacesMSBuildVersion)" />
<PackageVersion Include="Microsoft.CSharp" Version="$(MicrosoftCSharpVersion)" />
<PackageVersion Include="Microsoft.Css.Parser" Version="$(MicrosoftCssParserVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelVersion)" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingVersion)" />
<PackageVersion Include="Microsoft.NET.Sdk.Razor.SourceGenerators.Transport" Version="$(Benchmarks_BaselineSourceGeneratorsVersion)" />
<PackageVersion Include="Moq" Version="$(MoqVersion)" />
<PackageVersion Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
@ -20,7 +23,6 @@
<PackageVersion Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
<PackageVersion Include="xunit.assert" Version="$(XunitAssertVersion)" />
<PackageVersion Include="xunit.extensibility.execution" Version="$(XunitExtensibilityExecutionVersion)" />
<!-- Temporarily force analyzers to match compiler version https://github.com/dotnet/razor-tooling/issues/6758 -->
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="$(Tooling_MicrosoftCodeAnalysisAnalyzersPackageVersion)" NoWarn="NU1608" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="$(Tooling_MicrosoftCodeAnalysisNetAnalyzersPackageVersion)" NoWarn="NU1608" />

Просмотреть файл

@ -0,0 +1,146 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Razor.Microbenchmarks.Generator;
public class Benchmarks
{
ProjectSetup.RazorProject? _project;
public enum ChangeKind { Independent, DependentIgnorable, Dependent };
[ParamsAllUnlessDebug(ChangeKind.Independent)]
public ChangeKind Change { get; set; }
public enum StartupKind { Warm, Cold };
[ParamsAllUnlessDebug(StartupKind.Warm)]
public StartupKind Startup { get; set; }
[ModuleInitializer]
public static void LoadMSBuild() => MSBuildLocator.RegisterDefaults();
[GlobalSetup]
public void Setup()
{
_project = ProjectSetup.GetRazorProject(cold: Startup == StartupKind.Cold);
}
[Benchmark]
public void RunBenchmark()
{
var compilation = _project!.Compilation;
var driver = _project!.GeneratorDriver;
driver = RazorChange();
driver = driver.RunGenerators(compilation);
var result = driver.GetRunResult();
Debug.Assert(result.Diagnostics.IsDefaultOrEmpty);
}
private GeneratorDriver RazorChange()
{
var driver = _project!.GeneratorDriver;
var newRazorFile = GetNewRazorFile();
var existingRazorFile = GetExistingRazorFile();
if (newRazorFile is not null && existingRazorFile is not null)
{
driver = driver.ReplaceAdditionalText(existingRazorFile, newRazorFile);
}
else if (newRazorFile is not null)
{
driver = driver.AddAdditionalTexts(ImmutableArray.Create(newRazorFile));
}
else if (existingRazorFile is not null)
{
driver = driver.RemoveAdditionalTexts(ImmutableArray.Create(existingRazorFile));
}
return driver;
}
private AdditionalText? GetNewRazorFile()
{
if (Change == ChangeKind.Independent)
{
return new ProjectSetup.InMemoryAdditionalText(IndependentRazorFile, "Pages/Generated/0.razor");
}
else if (Change == ChangeKind.DependentIgnorable)
{
return new ProjectSetup.InMemoryAdditionalText(DependentIgnorableRazorFile, "Pages/Counter.razor");
}
else
{
return new ProjectSetup.InMemoryAdditionalText(DependentRazorFile, "Pages/Counter.razor");
}
}
private AdditionalText? GetExistingRazorFile()
{
if (Change == ChangeKind.Independent)
{
return _project!.AdditionalTexts.Single(a => a.Path.EndsWith("\\0.razor", StringComparison.OrdinalIgnoreCase));
}
else
{
return _project!.AdditionalTexts.Single(a => a.Path.EndsWith("Counter.razor", StringComparison.OrdinalIgnoreCase));
}
}
private const string IndependentRazorFile = "<h1>Independent File</h1>";
private const string DependentIgnorableRazorFile = """
@page "/counter"
<PageTitle>Counter edited</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int IncrementAmount { get; set; } = 1;
private int currentCount = 0;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}
""";
private const string DependentRazorFile = """
@page "/counter"
<PageTitle>Counter edited</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
""";
}

Просмотреть файл

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>
<ItemGroup>
<Compile Remove="SampleApp\**" />
<EmbeddedResource Remove="SampleApp\**" />
<None Remove="SampleApp\**" />
<Content Include="SampleApp\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" />
<PackageReference Include="Microsoft.Build.Locator" />
</ItemGroup>
<!-- Reference the local source generator when building in regular configurations -->
<ItemGroup Condition="'$(Configuration)' != 'Release_Nuget'">
<ProjectReference Include="..\..\Microsoft.NET.Sdk.Razor.SourceGenerators\Microsoft.NET.Sdk.Razor.SourceGenerators.csproj" />
</ItemGroup>
<!-- Grab the nuget package and reference its generator when building in Release_Nuget -->
<ItemGroup Condition="'$(Configuration)' == 'Release_Nuget'">
<PackageReference Include="Microsoft.NET.Sdk.Razor.SourceGenerators.Transport" GeneratePathProperty="true" />
<Reference Include="$(PkgMicrosoft_NET_Sdk_Razor_SourceGenerators_Transport)\source-generators\*.dll" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using BenchmarkDotNet.Attributes;
namespace Microsoft.AspNetCore.Razor.Microbenchmarks.Generator;
internal class ParamsAllUnlessDebugAttribute
#if DEBUG
: ParamsAttribute
#else
: ParamsAllValuesAttribute
#endif
{
internal ParamsAllUnlessDebugAttribute(params object[] args)
#if DEBUG
: base(args[0])
#else
: base()
#endif
{
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Exporters.Csv;
using BenchmarkDotNet.Running;
Job baseJob = Job.Default;
#if DEBUG
baseJob = baseJob
.WithIterationCount(1)
.RunOncePerIteration()
.WithToolchain(new BenchmarkDotNet.Toolchains.InProcess.Emit.InProcessEmitToolchain(TimeSpan.FromHours(1.0), logOutput: true));
#endif
var config = ManualConfig.CreateMinimumViable()
.AddJob(baseJob.WithCustomBuildConfiguration("Release").WithId("Current"))
.AddJob(baseJob.WithCustomBuildConfiguration("Release_Nuget").WithId("Baseline").WithBaseline(true))
.StopOnFirstError(true)
.AddExporter(CsvExporter.Default);
var results = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
var reports =
from summary in results
from report in summary.Reports
where !summary.IsBaseline(report.BenchmarkCase)
let baselineCase = summary.GetBaseline(summary.GetLogicalGroupKey(report.BenchmarkCase))
let baseline = summary.Reports.Single(r => r.BenchmarkCase == baselineCase)
select (report, baseline);
int exitCode = 0;
foreach ((var benchmark, var baseline) in reports)
{
// Note: there are actual statistical tests we could do here, but this should suffice.
// We can invest more if we see consistent false positives
var ratio = benchmark.ResultStatistics.Mean / baseline.ResultStatistics.Mean;
if (ratio > 1.1)
{
Console.WriteLine();
Console.WriteLine("Benchmark may have regressed!");
Console.WriteLine(benchmark.BenchmarkCase.DisplayInfo);
exitCode--;
}
}
return exitCode;

Просмотреть файл

@ -0,0 +1,138 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
using Microsoft.NET.Sdk.Razor.SourceGenerators;
namespace Microsoft.AspNetCore.Razor.Microbenchmarks.Generator;
internal static class ProjectSetup
{
internal static async Task<RazorProject> GetRazorProjectAsync(bool cold = true)
{
var workspace = MSBuildWorkspace.Create();
var project = await workspace.OpenProjectAsync("SampleApp/SampleApp.csproj");
// remove any generators from the project as we don't want generated files in our initial compilation
foreach (var analyzerRef in project.AnalyzerReferences)
{
project = project.RemoveAnalyzerReference(analyzerRef);
}
// get the constituent parts
var compilation = await project.GetCompilationAndCheckSuccess();
List<AdditionalText> additionalTexts = new List<AdditionalText>();
foreach (var additionalDocument in project.AdditionalDocuments)
{
var text = await additionalDocument.GetTextAsync();
additionalTexts.Add(new InMemoryAdditionalText(text, additionalDocument.FilePath!));
}
var parseOptions = (CSharpParseOptions)project.ParseOptions!;
var optionsProvider = new TargetPathAnalyzerConfigOptionsProvider(project.AnalyzerOptions.AnalyzerConfigOptionsProvider);
// create the generator driver we'll use for the tests
// the generator we use will be dependent on the build configuration the benchmark is built in
GeneratorDriver driver = CSharpGeneratorDriver.Create(generators: new[] { new RazorSourceGenerator().AsSourceGenerator() },
additionalTexts: additionalTexts,
parseOptions: parseOptions,
optionsProvider: optionsProvider);
// if we request a warm project, run the driver once through to start with, priming the caches
if (!cold)
{
driver = driver.RunGenerators(compilation);
}
return new(driver, compilation, additionalTexts.ToImmutableArray(), parseOptions, optionsProvider);
}
internal static RazorProject GetRazorProject(bool cold = true) => Task.Run(() => GetRazorProjectAsync(cold)).GetAwaiter().GetResult();
public static async Task<Compilation> GetCompilationAndCheckSuccess(this Project project)
{
var comp = await project.GetCompilationAsync();
var diagnostics = comp!.GetDiagnostics();
if (diagnostics.Any(d => d.Severity != DiagnosticSeverity.Hidden))
{
//Debug.Fail("Compilation contained non-hidden diagnostics");
}
return comp;
}
internal record RazorProject(GeneratorDriver GeneratorDriver, Compilation Compilation, ImmutableArray<AdditionalText> AdditionalTexts, CSharpParseOptions ParseOptions, AnalyzerConfigOptionsProvider OptionsProvider);
internal sealed class InMemoryAdditionalText : AdditionalText
{
private readonly SourceText _text;
public InMemoryAdditionalText(SourceText text, string path)
{
_text = text;
Path = path;
}
public InMemoryAdditionalText(string text, string path)
{
_text = SourceText.From(text, Encoding.UTF8);
Path = path;
}
public override string Path { get; }
public override SourceText? GetText(CancellationToken cancellationToken = default) => _text;
}
/// <summary>
/// An options provider that will add the required razor metadata if it's missing.
/// </summary>
internal sealed class TargetPathAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
{
private readonly AnalyzerConfigOptionsProvider _provider;
public TargetPathAnalyzerConfigOptionsProvider(AnalyzerConfigOptionsProvider provider)
{
_provider = provider;
}
public override AnalyzerConfigOptions GlobalOptions { get => _provider.GlobalOptions; }
public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => _provider.GetOptions(tree);
public override AnalyzerConfigOptions GetOptions(AdditionalText textFile)
{
return new TargetPathAnalyzerOptions(textFile.Path, _provider.GetOptions(textFile));
}
internal class TargetPathAnalyzerOptions : AnalyzerConfigOptions
{
private readonly string _targetPath;
private readonly AnalyzerConfigOptions _baseOptions;
public TargetPathAnalyzerOptions(string name, AnalyzerConfigOptions baseOptions)
{
_targetPath = Convert.ToBase64String(Encoding.UTF8.GetBytes(name));
_baseOptions = baseOptions;
}
public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
{
if (!_baseOptions.TryGetValue(key, out value))
{
value = key == "build_metadata.AdditionalFiles.TargetPath" ? _targetPath : string.Empty;
}
return true;
}
}
}
}

Просмотреть файл

@ -0,0 +1 @@
root = true

Просмотреть файл

@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

Просмотреть файл

@ -0,0 +1,12 @@
namespace SampleApp.Data;
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}

Просмотреть файл

@ -0,0 +1,19 @@
namespace SampleApp.Data;
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public Task<WeatherForecast[]> GetForecastAsync(DateOnly startDate)
{
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray());
}
}

Просмотреть файл

@ -0,0 +1,21 @@
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int IncrementAmount { get; set; } = 1;
private int currentCount = 0;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}

Просмотреть файл

@ -0,0 +1,42 @@
@page
@model SampleApp.Pages.ErrorModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
</head>
<body>
<div class="main">
<div class="content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</div>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,26 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace SampleApp.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}

Просмотреть файл

@ -0,0 +1,47 @@
@page "/fetchdata"
@using SampleApp.Data
@inject WeatherForecastService ForecastService
<PageTitle>Weather forecast</PageTitle>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
}
}

Просмотреть файл

@ -0,0 +1,2 @@
@page "/0"
<h1>Page 0 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/1"
<h1>Page 1 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/10"
<h1>Page 10 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/11"
<h1>Page 11 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/12"
<h1>Page 12 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/13"
<h1>Page 13 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/14"
<h1>Page 14 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/15"
<h1>Page 15 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/16"
<h1>Page 16 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/17"
<h1>Page 17 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/18"
<h1>Page 18 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/19"
<h1>Page 19 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/2"
<h1>Page 2 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/20"
<h1>Page 20 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/21"
<h1>Page 21 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/22"
<h1>Page 22 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/23"
<h1>Page 23 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/24"
<h1>Page 24 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/25"
<h1>Page 25 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/26"
<h1>Page 26 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/27"
<h1>Page 27 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/28"
<h1>Page 28 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/29"
<h1>Page 29 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/3"
<h1>Page 3 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/30"
<h1>Page 30 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/31"
<h1>Page 31 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/32"
<h1>Page 32 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/33"
<h1>Page 33 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/34"
<h1>Page 34 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/35"
<h1>Page 35 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/36"
<h1>Page 36 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/37"
<h1>Page 37 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/38"
<h1>Page 38 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/39"
<h1>Page 39 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/4"
<h1>Page 4 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/40"
<h1>Page 40 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/41"
<h1>Page 41 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/42"
<h1>Page 42 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/43"
<h1>Page 43 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/44"
<h1>Page 44 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/45"
<h1>Page 45 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/46"
<h1>Page 46 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/47"
<h1>Page 47 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/48"
<h1>Page 48 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/49"
<h1>Page 49 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/5"
<h1>Page 5 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/50"
<h1>Page 50 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/51"
<h1>Page 51 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/52"
<h1>Page 52 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/53"
<h1>Page 53 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/54"
<h1>Page 54 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/55"
<h1>Page 55 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/56"
<h1>Page 56 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/57"
<h1>Page 57 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/58"
<h1>Page 58 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/59"
<h1>Page 59 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/6"
<h1>Page 6 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/60"
<h1>Page 60 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/61"
<h1>Page 61 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/62"
<h1>Page 62 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/63"
<h1>Page 63 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/64"
<h1>Page 64 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/65"
<h1>Page 65 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/66"
<h1>Page 66 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/67"
<h1>Page 67 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/68"
<h1>Page 68 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/69"
<h1>Page 69 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/7"
<h1>Page 7 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/70"
<h1>Page 70 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/71"
<h1>Page 71 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/72"
<h1>Page 72 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/73"
<h1>Page 73 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/74"
<h1>Page 74 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/75"
<h1>Page 75 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/76"
<h1>Page 76 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/77"
<h1>Page 77 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/78"
<h1>Page 78 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/79"
<h1>Page 79 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/8"
<h1>Page 8 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/80"
<h1>Page 80 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/81"
<h1>Page 81 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/82"
<h1>Page 82 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/83"
<h1>Page 83 </h1>

Просмотреть файл

@ -0,0 +1,2 @@
@page "/84"
<h1>Page 84 </h1>

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