Merge branch 'master' into merge/release/5.0-preview8-to-master

This commit is contained in:
Pranav K 2020-08-04 16:21:58 -07:00 committed by GitHub
commit 84b30133a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
522 changed files with 15320 additions and 3825 deletions

View File

@ -4,6 +4,11 @@
# Daily Tests for ASP.NET Core SignalR
# These use Sauce Labs resources, hence they run daily rather than per-commit.
variables:
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
- group: DotNet-MSRC-Storage
- group: AzureDevOps-Artifact-Feeds-Pats
# The only Daily Tests we have run in Sauce Labs and only need to run on one machine (because they just trigger SauceLabs)
# Hence we use the 'default-build.yml' template because it represents a single phase
jobs:

66
.github/ISSUE_TEMPLATE/razor_tooling.md vendored Normal file
View File

@ -0,0 +1,66 @@
---
name: 🐞 Razor Tooling Bug report
about: Report an issue about something that is not working in the new Razor tooling
labels: area-razor.tooling, feature-razor.vs
---
<!--
More information on our issue management policies can be found here: https://aka.ms/aspnet/issue-policies
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting **non-security** bugs and feature requests.
If you believe you have an issue that affects the SECURITY of the platform, please do NOT create an issue and instead email your issue details to secure@microsoft.com. Your report may be eligible for our [bug bounty](https://www.microsoft.com/en-us/msrc/bounty-dot-net-core) but ONLY if it is reported through email.
For other types of questions, consider using [StackOverflow](https://stackoverflow.com).
-->
<!-- NOTE: This issue template is meant specifically to be used for issues with the new experimental Razor tooling experience provided in Visual Studio's Preview Feature pane -->
### Describe the bug
A clear and concise description of what the bug is.
### To Reproduce
<!--
We ❤ code! Point us to a minimalistic repro project hosted in a GitHub repo.
For a repro project, create a new ASP.NET Core project using the template of your your choice, apply the minimum required code to result in the issue you're observing.
We will close this issue if:
- the repro project you share with us is complex. We can't investigate custom projects, so don't point us to such, please.
- if we will not be able to repro the behavior you're reporting
-->
### Logs & Exceptions
Please collect the data below before reporting your issue to aid us in diagnosing the root cause.
#### Activity log
[Here](https://docs.microsoft.com/en-us/visualstudio/extensibility/how-to-use-the-activity-log?view=vs-2019#to-examine-the-activity-log) are the instructions on how to generate/acquire one.
#### Razor Language Server Client log
<!-- In Visual Studio's `Output` window, the drop-down contains a `Razor Language Server Client` item. Include that below. -->
<details>
<summary>Razor Language Server Client Log Output</summary>
Paste log output here
</details>
#### HTML Language Server Client log
<!-- In Visual Studio's `Output` window, the drop-down contains a `HtmlyLanguageClient` item. Include that below. -->
<details>
<summary>HTML Language Server Client Log Output</summary>
Paste log output here
</details>
### Further technical details
- VS version (Help => About Microsoft Visual Studio, i.e. 16.8.0 Preview 1 30313.27...). If in Codespaces there will be two versions (server and client), please provide both.
- Scenario (Local, LiveShare, Codespaces)
### Pre-requisite checklist
- [ ] Steps to reproduce the issue
- [ ] Visual Studio Activity Log attached.
- [ ] Razor Language Server client logs included.
- [ ] HTML Language Server client logs included

File diff suppressed because it is too large Load Diff

View File

@ -151,9 +151,7 @@
Include="$(RepoRoot)src\Analyzers\Internal.AspNetCore.Analyzers\src\Internal.AspNetCore.Analyzers.csproj"
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
PrivateAssets="All"
Version="$(InternalAspNetCoreAnalyzersPackageVersion)"
IsImplicitlyDefined="true" />
PrivateAssets="All" />
</ItemGroup>
<!-- Compilation options which apply to all languages. Language-specific options should be set in eng/targets/$(lang).Common.props -->

View File

@ -2,9 +2,6 @@
<configuration>
<packageSources>
<clear />
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
<add key="darc-pub-dotnet-blazor-cc44960" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-blazor-cc449601/nuget/v3/index.json" />
<!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet5" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json" />

View File

@ -4,6 +4,22 @@ Building ASP.NET Core from source allows you to tweak and customize ASP.NET Core
See <https://github.com/dotnet/aspnetcore/labels/area-infrastructure> for known issues and to track ongoing work.
## Clone the source code
ASP.NET Core uses git submodules to include the source from a few other projects.
For a new copy of the project, run:
```ps1
git clone --recursive https://github.com/dotnet/aspnetcore
```
To update an existing copy, run:
```ps1
git submodule update --init --recursive
```
## Install pre-requisites
### Windows
@ -22,7 +38,8 @@ Building ASP.NET Core on Windows requires:
However, any Visual Studio 2019 instance that meets the requirements should be fine. See [global.json](/global.json)
and [eng/scripts/vs.json](/eng/scripts/vs.json) for those requirements. By default, the script will install Visual Studio Enterprise Edition, however you can use a different edition by passing the `-Edition` flag.
* Git. <https://git-scm.org>
* NodeJS. LTS version of 10.14.2 or newer <https://nodejs.org>
* NodeJS. LTS version of 10.14.2 or newer <https://nodejs.org>.
* Install yarn globally (`npm install -g yarn`)
* Java Development Kit 11 or newer. Either:
* OpenJDK <https://jdk.java.net/>
* Oracle's JDK <https://www.oracle.com/technetwork/java/javase/downloads/index.html>
@ -52,22 +69,6 @@ Building ASP.NET Core on macOS or Linux requires:
* OpenJDK <https://jdk.java.net/>
* Oracle's JDK <https://www.oracle.com/technetwork/java/javase/downloads/index.html>
## Clone the source code
ASP.NET Core uses git submodules to include the source from a few other projects.
For a new copy of the project, run:
```ps1
git clone --recursive https://github.com/dotnet/aspnetcore
```
To update an existing copy, run:
```ps1
git submodule update --init --recursive
```
**NOTE** some ISPs have been know to use web filtering software that has caused issues with git repository cloning, if you experience issues cloning this repo please review <https://help.github.com/en/github/authenticating-to-github/using-ssh-over-the-https-port>
## Building in Visual Studio
@ -86,6 +87,9 @@ Before opening our .sln/.slnf files in Visual Studio or VS Code, you need to per
> :bulb: Pro tip: you will also want to run this command after pulling large sets of changes. On the master
> branch, we regularly update the versions of .NET Core SDK required to build the repo.
> You will need to restart Visual Studio every time we update the .NET Core SDK.
> To allow executing the setup script, you may need to update the execution policy on your machine.
You can do so by running the `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser` command
in PowerShell. For more information on execution policies, you can read the [execution policy docs](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy).
2. Use the `startvs.cmd` script to open Visual Studio .sln/.slnf files. This script first sets the required
environment variables.
@ -128,6 +132,18 @@ Executing `.\restore.cmd` or `.\build.cmd` may produce these errors:
In most cases, this is because the option _Use previews of the .NET Core SDK_ in VS2019 is not checked. Start Visual Studio, go to _Tools > Options_ and check _Use previews of the .NET Core SDK_ under _Environment > Preview Features_.
### Common error: HTTP Error 500.33 - ANCM Request Handler Load Failure
The [ASP.NET Core Module](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module) (ANCM) for IIS is not supported when running projects in this repository.
After using `startvs.cmd` to open a solution in Visual Studio, the Kestrel web host option must be used (name of the project) and not IIS Express.
Example of running the `MvcSandbox` project:
`.\startvs.cmd .\src\Mvc\Mvc.sln`
![Web host options in Visual Studio](./vs-iis-express-aspnet-core-mvc-sandbox.jpg)
## Building with Visual Studio Code
Using Visual Studio Code with this repo requires setting environment variables on command line first.

View File

@ -19,7 +19,7 @@ This will restore, and then publish all the test project including some bootstra
## Overview of the helix usage in our pipelines
- Required queues: Windows10, OSX, Ubuntu1604
- Full queue matrix: Windows[7, 81, 10], Ubuntu[1604, 1804, 2004], Centos7, Debian[8,9], Redhat7, Fedora28, Arm64 (Win10, Debian9)
- Full queue matrix: Windows[7, 81, 10], Ubuntu[1604, 1804, 2004], Centos7, Debian9, Redhat7, Fedora28, Arm64 (Win10, Debian9)
- The queues are defined in [Helix.Common.props](https://github.com/dotnet/aspnetcore/blob/master/eng/targets/Helix.Common.props)
[aspnetcore-ci](https://dev.azure.com/dnceng/public/_build?definitionId=278) runs non quarantined tests against the required helix queues as a required PR check and all builds on all branches.

View File

@ -100,16 +100,64 @@ is changing to `Microsoft.AspNetCore.BetterThanOrange`, you would need to make t
</ItemGroup>
```
## Updating dependencies manually
## A darc cheatsheet
If the `dotnet-maestro` bot has not correctly updated the dependencies, it may be worthwhile running `darc` manually:
`darc` is a command-line tool that is used for dependency management in the dotnet ecosystem of repos. `darc` can be installed using the `darc-init` scripts located inside the `eng/common` directory. Once `darc` is installed, you'll need to set up the appropriate access tokens as outlined [in the official Darc docs](https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#setting-up-your-darc-client).
Once `darc` is installed and set-up, it can be used to modify the subscriptions and dependencies in a project.
**Getting the list of subscriptions in a repo**
Subscriptions are objects that define the ecosystem repos we are listening for updates to, the frequency we are looking for updates, and more.
```bash
darc get-subscriptions --target-branch master --target-repo aspnetcore$ --regex
```
**Disable/enable a subscription**
```bash
darc subscription-status --id {subscriptionIdHere} --enable
darc subscription-status --id {subscriptionIdHere} --disable
```
**Trigger a subscription**
Triggering a subscription will search for updates in its dependencies and open a PR in the target repo via the dotnet-maestro bot with these changes.
```bash
darc trigger-subscriptions --id {subscriptionIdHere}
```
**Manually update dependencies**
If the `dotnet-maestro` bot has not correctly updated the dependencies, `darc update-dependencies` may be used to update the dependencies manually. Note, you'll need to run the commands below in a separate branch and submit a PR with the changes. These are the things that the bot should do for you if you use `trigger-subscriptions` or automatically (when the subscription fires e.g. about 15 minutes after a dependency's build completes if `Update Frequency: EveryBuild`).
```bash
darc update-dependencies --channel '.NET Core 3.1 Release'
darc update-dependencies --channel '.NET 5 Dev' --source-repo efcore
```
Generally, using `trigger-subscriptions` is preferred for creating dependency updates instead of manually updating dependencies in your own PR.
**Toggling batchability of subscription**
Subscriptions can be batched. When a dependency update is detected, `darc` will bundle the commits for that update with existing dependency PRs. To toggle whether a subscription is batched or not, you will need to use the `update-subscription` command.
```bash
darc update-subscription --id {subscriptionIdHere}
```
Your shell's default editor will open and allow you to edit the metadata of the subscription.
To disable batching, set `Batchable` to `False` and update the `Merge Policies` section with the following YAML.
```
- Name: Standard
Properties: {}
```
To enable batching, set `Batchable` to `True` and remove any `Merge Policies` set on the subscription.
Note: Merge policies can only be set on unbatched subscriptions. Be sure to set/unset the `Merge Policies` field properly as you toggle batchability.
1. Install `darc` as described in <https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#setting-up-your-darc-client>
2. Run `darc update-dependencies --channel '.NET Core 3.1 Release'`
* Use `'trigger-subscriptions'` to prod the bot to create a PR if you do not want to make local changes
* Use `'.NET 3 Eng''` to update dependencies from dotnet/arcade
* Use `'.NET Eng - Latest'` to update dependencies from dotnet/arcade in the `master` branch
* Use `'VS Master'` to update dependencies from dotnet/roslyn in the `master` branch
* Use `'.NET 5 Dev'` to update dependencies from dotnet/efcore or dotnet/runtime in the `master` branch
3. `git diff` to confirm the tool's changes
4. Proceed with usual commit and PR

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -44,10 +44,6 @@
<!-- Project selection can be overridden on the command line by passing in -projects. -->
<When Condition="'$(ProjectToBuild)' != ''">
<ItemGroup>
<!-- Include RTMVersions.csproj unless this invocation is building RepoTasks.csproj or won't compile C#. -->
<ProjectToBuild Include="$(RepoRoot)eng\RTMVersions\RTMVersions.csproj"
Exclude="@(ProjectToExclude)"
Condition=" $(ProjectToBuild.EndsWith('.csproj')) AND !$(ProjectToBuild.EndsWith('RepoTasks.csproj')) " />
<ProjectToBuild Include="$(ProjectToBuild)" Exclude="@(ProjectToExclude);$(RepoRoot)**\bin\**\*;$(RepoRoot)**\obj\**\*">
<RestoreInParallel Condition="'%(Extension)' == '.npmproj'">false</RestoreInParallel>
</ProjectToBuild>
@ -125,11 +121,8 @@
<!--
Use caution to avoid deep recursion. If the globbing pattern picks up something which exceeds MAX_PATH,
the entire pattern will silently fail to evaluate correctly.
Include RTMVersions.csproj when building any managed projects.
-->
<DotNetProjects Include="
$(RepoRoot)eng\RTMVersions\RTMVersions.csproj;
$(RepoRoot)src\Framework\App.Ref\src\Microsoft.AspNetCore.App.Ref.csproj;
$(RepoRoot)src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj;
$(RepoRoot)src\Framework\App.Ref.Internal\src\Microsoft.AspNetCore.App.Ref.Internal.csproj;
@ -178,7 +171,6 @@
$(RepoRoot)**\obj\**\*;"
Condition=" '$(BuildMainlyReferenceProviders)' != 'true' " />
<DotNetProjects Include="
$(RepoRoot)eng\RTMVersions\RTMVersions.csproj;
$(RepoRoot)src\DefaultBuilder\**\src\*.csproj;
$(RepoRoot)src\Features\JsonPatch\**\src\*.csproj;
$(RepoRoot)src\DataProtection\**\src\*.csproj;

View File

@ -33,7 +33,7 @@
-->
<Project>
<ItemGroup>
@(_ProjectReferenceProvider->'<ProjectReferenceProvider Include="%(Identity)" ProjectPath="%24(RepoRoot)%(ProjectFileRelativePath)" />', '%0A ')
@(_ProjectReferenceProvider->'<ProjectReferenceProvider Include="%(Identity)" ProjectPath="%24(RepoRoot)%(ProjectFileRelativePath)" />', '%0A ')
</ItemGroup>
</Project>
]]></ProjectListContent>

View File

@ -71,6 +71,8 @@ and are generated based on the last package release.
<LatestPackageReference Include="System.Reflection.Metadata" />
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" />
<LatestPackageReference Include="System.Runtime.InteropServices.RuntimeInformation" />
<!-- System.Security.AccessControl should only be referenced in Dependencies.props and RTMVersions.csproj. -->
<LatestPackageReference Include="System.Security.AccessControl" />
<LatestPackageReference Include="System.Security.Cryptography.Cng" />
<LatestPackageReference Include="System.Security.Cryptography.Pkcs" />
<LatestPackageReference Include="System.Security.Cryptography.Xml" />
@ -104,6 +106,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Runtime" />
<LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<LatestPackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" />
<LatestPackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
@ -159,6 +162,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="Libuv" />
<LatestPackageReference Include="MessagePack" />
<LatestPackageReference Include="MessagePackAnalyzer" />
<LatestPackageReference Include="Microsoft.Data.SqlClient" />
<LatestPackageReference Include="Microsoft.Identity.Web.UI" />
<LatestPackageReference Include="Microsoft.Graph" />
<LatestPackageReference Include="Mono.Cecil" />
@ -178,7 +182,6 @@ and are generated based on the last package release.
<LatestPackageReference Include="xunit.assert" />
<LatestPackageReference Include="xunit.extensibility.core" />
<LatestPackageReference Include="xunit.extensibility.execution" />
<LatestPackageReference Include="Microsoft.Data.SqlClient" />
</ItemGroup>
<ItemGroup Label="Dependencies with versions.">

View File

@ -5,160 +5,160 @@
-->
<Project>
<ItemGroup>
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.JsonPatch" ProjectPath="$(RepoRoot)src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.KeyDerivation\src\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection" ProjectPath="$(RepoRoot)src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\DataProtection\EntityFrameworkCore\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Extensions" ProjectPath="$(RepoRoot)src\DataProtection\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" ProjectPath="$(RepoRoot)src\DataProtection\StackExchangeRedis\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Antiforgery" ProjectPath="$(RepoRoot)src\Antiforgery\src\Microsoft.AspNetCore.Antiforgery.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.Abstractions" ProjectPath="$(RepoRoot)src\Hosting\Abstractions\src\Microsoft.AspNetCore.Hosting.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting" ProjectPath="$(RepoRoot)src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" ProjectPath="$(RepoRoot)src\Hosting\Server.Abstractions\src\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.TestHost" ProjectPath="$(RepoRoot)src\Hosting\TestHost\src\Microsoft.AspNetCore.TestHost.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.WindowsServices" ProjectPath="$(RepoRoot)src\Hosting\WindowsServices\src\Microsoft.AspNetCore.Hosting.WindowsServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Abstractions" ProjectPath="$(RepoRoot)src\Http\Authentication.Abstractions\src\Microsoft.AspNetCore.Authentication.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Core" ProjectPath="$(RepoRoot)src\Http\Authentication.Core\src\Microsoft.AspNetCore.Authentication.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.Net.Http.Headers" ProjectPath="$(RepoRoot)src\Http\Headers\src\Microsoft.Net.Http.Headers.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Abstractions" ProjectPath="$(RepoRoot)src\Http\Http.Abstractions\src\Microsoft.AspNetCore.Http.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Extensions" ProjectPath="$(RepoRoot)src\Http\Http.Extensions\src\Microsoft.AspNetCore.Http.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Features" ProjectPath="$(RepoRoot)src\Http\Http.Features\src\Microsoft.AspNetCore.Http.Features.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http" ProjectPath="$(RepoRoot)src\Http\Http\src\Microsoft.AspNetCore.Http.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Metadata" ProjectPath="$(RepoRoot)src\Http\Metadata\src\Microsoft.AspNetCore.Metadata.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Owin" ProjectPath="$(RepoRoot)src\Http\Owin\src\Microsoft.AspNetCore.Owin.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Routing.Abstractions" ProjectPath="$(RepoRoot)src\Http\Routing.Abstractions\src\Microsoft.AspNetCore.Routing.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Routing" ProjectPath="$(RepoRoot)src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.WebUtilities" ProjectPath="$(RepoRoot)src\Http\WebUtilities\src\Microsoft.AspNetCore.WebUtilities.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Html.Abstractions" ProjectPath="$(RepoRoot)src\Html\Abstractions\src\Microsoft.AspNetCore.Html.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" ProjectPath="$(RepoRoot)src\Identity\ApiAuthorization.IdentityServer\src\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity" ProjectPath="$(RepoRoot)src\Identity\Core\src\Microsoft.AspNetCore.Identity.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Identity\EntityFrameworkCore\src\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Identity.Core" ProjectPath="$(RepoRoot)src\Identity\Extensions.Core\src\Microsoft.Extensions.Identity.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Identity.Stores" ProjectPath="$(RepoRoot)src\Identity\Extensions.Stores\src\Microsoft.Extensions.Identity.Stores.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.Specification.Tests" ProjectPath="$(RepoRoot)src\Identity\Specification.Tests\src\Microsoft.AspNetCore.Identity.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.UI" ProjectPath="$(RepoRoot)src\Identity\UI\src\Microsoft.AspNetCore.Identity.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Connections.Abstractions" ProjectPath="$(RepoRoot)src\Servers\Connections.Abstractions\src\Microsoft.AspNetCore.Connections.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.HttpSys" ProjectPath="$(RepoRoot)src\Servers\HttpSys\src\Microsoft.AspNetCore.Server.HttpSys.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.IISIntegration" ProjectPath="$(RepoRoot)src\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.IIS" ProjectPath="$(RepoRoot)src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Core" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Libuv\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Quic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Sockets\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Certificate" ProjectPath="$(RepoRoot)src\Security\Authentication\Certificate\src\Microsoft.AspNetCore.Authentication.Certificate.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Cookies" ProjectPath="$(RepoRoot)src\Security\Authentication\Cookies\src\Microsoft.AspNetCore.Authentication.Cookies.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication" ProjectPath="$(RepoRoot)src\Security\Authentication\Core\src\Microsoft.AspNetCore.Authentication.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Facebook" ProjectPath="$(RepoRoot)src\Security\Authentication\Facebook\src\Microsoft.AspNetCore.Authentication.Facebook.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Google" ProjectPath="$(RepoRoot)src\Security\Authentication\Google\src\Microsoft.AspNetCore.Authentication.Google.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.JwtBearer" ProjectPath="$(RepoRoot)src\Security\Authentication\JwtBearer\src\Microsoft.AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" ProjectPath="$(RepoRoot)src\Security\Authentication\MicrosoftAccount\src\Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Negotiate" ProjectPath="$(RepoRoot)src\Security\Authentication\Negotiate\src\Microsoft.AspNetCore.Authentication.Negotiate.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.OAuth" ProjectPath="$(RepoRoot)src\Security\Authentication\OAuth\src\Microsoft.AspNetCore.Authentication.OAuth.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" ProjectPath="$(RepoRoot)src\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Twitter" ProjectPath="$(RepoRoot)src\Security\Authentication\Twitter\src\Microsoft.AspNetCore.Authentication.Twitter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.WsFederation" ProjectPath="$(RepoRoot)src\Security\Authentication\WsFederation\src\Microsoft.AspNetCore.Authentication.WsFederation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization" ProjectPath="$(RepoRoot)src\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization.Policy" ProjectPath="$(RepoRoot)src\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.CookiePolicy" ProjectPath="$(RepoRoot)src\Security\CookiePolicy\src\Microsoft.AspNetCore.CookiePolicy.csproj" />
<ProjectReferenceProvider Include="Microsoft.Web.Xdt.Extensions" ProjectPath="$(RepoRoot)src\SiteExtensions\Microsoft.Web.Xdt.Extensions\src\Microsoft.Web.Xdt.Extensions.csproj" />
<ProjectReferenceProvider Include="dotnet-getdocument" ProjectPath="$(RepoRoot)src\Tools\dotnet-getdocument\src\dotnet-getdocument.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Client" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Server" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Server\src\Microsoft.Extensions.ApiDescription.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
<ProjectReferenceProvider Include="GetDocument.Insider" ProjectPath="$(RepoRoot)src\Tools\GetDocumentInsider\src\GetDocumentInsider.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Logging.AzureAppServices" ProjectPath="$(RepoRoot)src\Logging.AzureAppServices\src\Microsoft.Extensions.Logging.AzureAppServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ConcurrencyLimiter" ProjectPath="$(RepoRoot)src\Middleware\ConcurrencyLimiter\src\Microsoft.AspNetCore.ConcurrencyLimiter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cors" ProjectPath="$(RepoRoot)src\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.Abstractions" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HeaderPropagation" ProjectPath="$(RepoRoot)src\Middleware\HeaderPropagation\src\Microsoft.AspNetCore.HeaderPropagation.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Middleware\HealthChecks.EntityFrameworkCore\src\Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\Middleware\HealthChecks\src\Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HostFiltering" ProjectPath="$(RepoRoot)src\Middleware\HostFiltering\src\Microsoft.AspNetCore.HostFiltering.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HttpOverrides" ProjectPath="$(RepoRoot)src\Middleware\HttpOverrides\src\Microsoft.AspNetCore.HttpOverrides.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HttpsPolicy" ProjectPath="$(RepoRoot)src\Middleware\HttpsPolicy\src\Microsoft.AspNetCore.HttpsPolicy.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization.Routing" ProjectPath="$(RepoRoot)src\Middleware\Localization.Routing\src\Microsoft.AspNetCore.Localization.Routing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization" ProjectPath="$(RepoRoot)src\Middleware\Localization\src\Microsoft.AspNetCore.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.MiddlewareAnalysis" ProjectPath="$(RepoRoot)src\Middleware\MiddlewareAnalysis\src\Microsoft.AspNetCore.MiddlewareAnalysis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.NodeServices" ProjectPath="$(RepoRoot)src\Middleware\NodeServices\src\Microsoft.AspNetCore.NodeServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" ProjectPath="$(RepoRoot)src\Middleware\ResponseCaching.Abstractions\src\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCaching" ProjectPath="$(RepoRoot)src\Middleware\ResponseCaching\src\Microsoft.AspNetCore.ResponseCaching.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCompression" ProjectPath="$(RepoRoot)src\Middleware\ResponseCompression\src\Microsoft.AspNetCore.ResponseCompression.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Rewrite" ProjectPath="$(RepoRoot)src\Middleware\Rewrite\src\Microsoft.AspNetCore.Rewrite.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Session" ProjectPath="$(RepoRoot)src\Middleware\Session\src\Microsoft.AspNetCore.Session.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SpaServices.Extensions" ProjectPath="$(RepoRoot)src\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SpaServices" ProjectPath="$(RepoRoot)src\Middleware\SpaServices\src\Microsoft.AspNetCore.SpaServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.StaticFiles" ProjectPath="$(RepoRoot)src\Middleware\StaticFiles\src\Microsoft.AspNetCore.StaticFiles.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.WebSockets" ProjectPath="$(RepoRoot)src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor.Language" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Razor.Language\src\Microsoft.AspNetCore.Razor.Language.csproj" />
<ProjectReferenceProvider Include="rzc" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Razor.Tools\src\Microsoft.AspNetCore.Razor.Tools.csproj" />
<ProjectReferenceProvider Include="Microsoft.CodeAnalysis.Razor" ProjectPath="$(RepoRoot)src\Razor\Microsoft.CodeAnalysis.Razor\src\Microsoft.CodeAnalysis.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.NET.Sdk.Razor" ProjectPath="$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\Microsoft.NET.Sdk.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor.Runtime" ProjectPath="$(RepoRoot)src\Razor\Razor.Runtime\src\Microsoft.AspNetCore.Razor.Runtime.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor" ProjectPath="$(RepoRoot)src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Abstractions" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Abstractions\src\Microsoft.AspNetCore.Mvc.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ApiExplorer" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ApiExplorer\src\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Core" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Cors" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Cors\src\Microsoft.AspNetCore.Mvc.Cors.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.DataAnnotations" ProjectPath="$(RepoRoot)src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Formatters.Json" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Formatters.Json\src\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Formatters.Xml\src\Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Localization" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Localization\src\Microsoft.AspNetCore.Mvc.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" ProjectPath="$(RepoRoot)src\Mvc\Mvc.NewtonsoftJson\src\Microsoft.AspNetCore.Mvc.NewtonsoftJson.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Razor.RuntimeCompilation\src\Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.RazorPages" ProjectPath="$(RepoRoot)src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.TagHelpers" ProjectPath="$(RepoRoot)src\Mvc\Mvc.TagHelpers\src\Microsoft.AspNetCore.Mvc.TagHelpers.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Testing" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ViewFeatures" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc" ProjectPath="$(RepoRoot)src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" ProjectPath="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureAD.UI\src\Microsoft.AspNetCore.Authentication.AzureAD.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" ProjectPath="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureADB2C.UI\src\Microsoft.AspNetCore.Authentication.AzureADB2C.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.AzureAppServices.HostingStartup" ProjectPath="$(RepoRoot)src\Azure\AzureAppServices.HostingStartup\src\Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.AzureAppServicesIntegration" ProjectPath="$(RepoRoot)src\Azure\AzureAppServicesIntegration\src\Microsoft.AspNetCore.AzureAppServicesIntegration.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Client.Core" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Client.Core\src\Microsoft.AspNetCore.SignalR.Client.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Client" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections.Client" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Http.Connections.Client\src\Microsoft.AspNetCore.Http.Connections.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections.Common" ProjectPath="$(RepoRoot)src\SignalR\common\Http.Connections.Common\src\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections" ProjectPath="$(RepoRoot)src\SignalR\common\Http.Connections\src\Microsoft.AspNetCore.Http.Connections.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.Json" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.MessagePack\src\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.NewtonsoftJson\src\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Common" ProjectPath="$(RepoRoot)src\SignalR\common\SignalR.Common\src\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Core" ProjectPath="$(RepoRoot)src\SignalR\server\Core\src\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR" ProjectPath="$(RepoRoot)src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Specification.Tests" ProjectPath="$(RepoRoot)src\SignalR\server\Specification.Tests\src\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" ProjectPath="$(RepoRoot)src\SignalR\server\StackExchangeRedis\src\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Authorization" ProjectPath="$(RepoRoot)src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components" ProjectPath="$(RepoRoot)src\Components\Components\src\Microsoft.AspNetCore.Components.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" />
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web.Extensions" ProjectPath="$(RepoRoot)src\Components\Web.Extensions\src\Microsoft.AspNetCore.Components.Web.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Authentication.WebAssembly.Msal" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj" />
<ProjectReferenceProvider Include="Microsoft.JSInterop.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\JSInterop\src\Microsoft.JSInterop.WebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.NET.Sdk.BlazorWebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Server" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.FileProviders.Embedded" ProjectPath="$(RepoRoot)src\FileProviders\Embedded\src\Microsoft.Extensions.FileProviders.Embedded.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Configuration.KeyPerFile" ProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\src\Microsoft.Extensions.Configuration.KeyPerFile.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization.Abstractions" ProjectPath="$(RepoRoot)src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization" ProjectPath="$(RepoRoot)src\Localization\Localization\src\Microsoft.Extensions.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ObjectPool" ProjectPath="$(RepoRoot)src\ObjectPool\src\Microsoft.Extensions.ObjectPool.csproj" />
<ProjectReferenceProvider Include="Microsoft.JSInterop" ProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.WebEncoders" ProjectPath="$(RepoRoot)src\WebEncoders\src\Microsoft.Extensions.WebEncoders.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" ProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\src\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\src\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Testing" ProjectPath="$(RepoRoot)src\Testing\src\Microsoft.AspNetCore.Testing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.JsonPatch" ProjectPath="$(RepoRoot)src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.KeyDerivation\src\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection" ProjectPath="$(RepoRoot)src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\DataProtection\EntityFrameworkCore\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Extensions" ProjectPath="$(RepoRoot)src\DataProtection\Extensions\src\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" ProjectPath="$(RepoRoot)src\DataProtection\StackExchangeRedis\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Antiforgery" ProjectPath="$(RepoRoot)src\Antiforgery\src\Microsoft.AspNetCore.Antiforgery.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.Abstractions" ProjectPath="$(RepoRoot)src\Hosting\Abstractions\src\Microsoft.AspNetCore.Hosting.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting" ProjectPath="$(RepoRoot)src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" ProjectPath="$(RepoRoot)src\Hosting\Server.Abstractions\src\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.TestHost" ProjectPath="$(RepoRoot)src\Hosting\TestHost\src\Microsoft.AspNetCore.TestHost.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Hosting.WindowsServices" ProjectPath="$(RepoRoot)src\Hosting\WindowsServices\src\Microsoft.AspNetCore.Hosting.WindowsServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Abstractions" ProjectPath="$(RepoRoot)src\Http\Authentication.Abstractions\src\Microsoft.AspNetCore.Authentication.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Core" ProjectPath="$(RepoRoot)src\Http\Authentication.Core\src\Microsoft.AspNetCore.Authentication.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.Net.Http.Headers" ProjectPath="$(RepoRoot)src\Http\Headers\src\Microsoft.Net.Http.Headers.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Abstractions" ProjectPath="$(RepoRoot)src\Http\Http.Abstractions\src\Microsoft.AspNetCore.Http.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Extensions" ProjectPath="$(RepoRoot)src\Http\Http.Extensions\src\Microsoft.AspNetCore.Http.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Features" ProjectPath="$(RepoRoot)src\Http\Http.Features\src\Microsoft.AspNetCore.Http.Features.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http" ProjectPath="$(RepoRoot)src\Http\Http\src\Microsoft.AspNetCore.Http.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Metadata" ProjectPath="$(RepoRoot)src\Http\Metadata\src\Microsoft.AspNetCore.Metadata.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Owin" ProjectPath="$(RepoRoot)src\Http\Owin\src\Microsoft.AspNetCore.Owin.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Routing.Abstractions" ProjectPath="$(RepoRoot)src\Http\Routing.Abstractions\src\Microsoft.AspNetCore.Routing.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Routing" ProjectPath="$(RepoRoot)src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.WebUtilities" ProjectPath="$(RepoRoot)src\Http\WebUtilities\src\Microsoft.AspNetCore.WebUtilities.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Html.Abstractions" ProjectPath="$(RepoRoot)src\Html\Abstractions\src\Microsoft.AspNetCore.Html.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" ProjectPath="$(RepoRoot)src\Identity\ApiAuthorization.IdentityServer\src\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity" ProjectPath="$(RepoRoot)src\Identity\Core\src\Microsoft.AspNetCore.Identity.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Identity\EntityFrameworkCore\src\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Identity.Core" ProjectPath="$(RepoRoot)src\Identity\Extensions.Core\src\Microsoft.Extensions.Identity.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Identity.Stores" ProjectPath="$(RepoRoot)src\Identity\Extensions.Stores\src\Microsoft.Extensions.Identity.Stores.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.Specification.Tests" ProjectPath="$(RepoRoot)src\Identity\Specification.Tests\src\Microsoft.AspNetCore.Identity.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Identity.UI" ProjectPath="$(RepoRoot)src\Identity\UI\src\Microsoft.AspNetCore.Identity.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Connections.Abstractions" ProjectPath="$(RepoRoot)src\Servers\Connections.Abstractions\src\Microsoft.AspNetCore.Connections.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.HttpSys" ProjectPath="$(RepoRoot)src\Servers\HttpSys\src\Microsoft.AspNetCore.Server.HttpSys.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.IISIntegration" ProjectPath="$(RepoRoot)src\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.IIS" ProjectPath="$(RepoRoot)src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Core" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Libuv\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Quic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Experimental.Quic.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Sockets\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Certificate" ProjectPath="$(RepoRoot)src\Security\Authentication\Certificate\src\Microsoft.AspNetCore.Authentication.Certificate.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Cookies" ProjectPath="$(RepoRoot)src\Security\Authentication\Cookies\src\Microsoft.AspNetCore.Authentication.Cookies.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication" ProjectPath="$(RepoRoot)src\Security\Authentication\Core\src\Microsoft.AspNetCore.Authentication.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Facebook" ProjectPath="$(RepoRoot)src\Security\Authentication\Facebook\src\Microsoft.AspNetCore.Authentication.Facebook.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Google" ProjectPath="$(RepoRoot)src\Security\Authentication\Google\src\Microsoft.AspNetCore.Authentication.Google.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.JwtBearer" ProjectPath="$(RepoRoot)src\Security\Authentication\JwtBearer\src\Microsoft.AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" ProjectPath="$(RepoRoot)src\Security\Authentication\MicrosoftAccount\src\Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Negotiate" ProjectPath="$(RepoRoot)src\Security\Authentication\Negotiate\src\Microsoft.AspNetCore.Authentication.Negotiate.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.OAuth" ProjectPath="$(RepoRoot)src\Security\Authentication\OAuth\src\Microsoft.AspNetCore.Authentication.OAuth.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" ProjectPath="$(RepoRoot)src\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Twitter" ProjectPath="$(RepoRoot)src\Security\Authentication\Twitter\src\Microsoft.AspNetCore.Authentication.Twitter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.WsFederation" ProjectPath="$(RepoRoot)src\Security\Authentication\WsFederation\src\Microsoft.AspNetCore.Authentication.WsFederation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization" ProjectPath="$(RepoRoot)src\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authorization.Policy" ProjectPath="$(RepoRoot)src\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.CookiePolicy" ProjectPath="$(RepoRoot)src\Security\CookiePolicy\src\Microsoft.AspNetCore.CookiePolicy.csproj" />
<ProjectReferenceProvider Include="Microsoft.Web.Xdt.Extensions" ProjectPath="$(RepoRoot)src\SiteExtensions\Microsoft.Web.Xdt.Extensions\src\Microsoft.Web.Xdt.Extensions.csproj" />
<ProjectReferenceProvider Include="dotnet-getdocument" ProjectPath="$(RepoRoot)src\Tools\dotnet-getdocument\src\dotnet-getdocument.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Client" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Server" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Server\src\Microsoft.Extensions.ApiDescription.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
<ProjectReferenceProvider Include="GetDocument.Insider" ProjectPath="$(RepoRoot)src\Tools\GetDocumentInsider\src\GetDocument.Insider.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Logging.AzureAppServices" ProjectPath="$(RepoRoot)src\Logging.AzureAppServices\src\Microsoft.Extensions.Logging.AzureAppServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ConcurrencyLimiter" ProjectPath="$(RepoRoot)src\Middleware\ConcurrencyLimiter\src\Microsoft.AspNetCore.ConcurrencyLimiter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cors" ProjectPath="$(RepoRoot)src\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.Abstractions" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HeaderPropagation" ProjectPath="$(RepoRoot)src\Middleware\HeaderPropagation\src\Microsoft.AspNetCore.HeaderPropagation.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Middleware\HealthChecks.EntityFrameworkCore\src\Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\Middleware\HealthChecks\src\Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HostFiltering" ProjectPath="$(RepoRoot)src\Middleware\HostFiltering\src\Microsoft.AspNetCore.HostFiltering.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HttpOverrides" ProjectPath="$(RepoRoot)src\Middleware\HttpOverrides\src\Microsoft.AspNetCore.HttpOverrides.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HttpsPolicy" ProjectPath="$(RepoRoot)src\Middleware\HttpsPolicy\src\Microsoft.AspNetCore.HttpsPolicy.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization.Routing" ProjectPath="$(RepoRoot)src\Middleware\Localization.Routing\src\Microsoft.AspNetCore.Localization.Routing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization" ProjectPath="$(RepoRoot)src\Middleware\Localization\src\Microsoft.AspNetCore.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.MiddlewareAnalysis" ProjectPath="$(RepoRoot)src\Middleware\MiddlewareAnalysis\src\Microsoft.AspNetCore.MiddlewareAnalysis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.NodeServices" ProjectPath="$(RepoRoot)src\Middleware\NodeServices\src\Microsoft.AspNetCore.NodeServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" ProjectPath="$(RepoRoot)src\Middleware\ResponseCaching.Abstractions\src\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCaching" ProjectPath="$(RepoRoot)src\Middleware\ResponseCaching\src\Microsoft.AspNetCore.ResponseCaching.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCompression" ProjectPath="$(RepoRoot)src\Middleware\ResponseCompression\src\Microsoft.AspNetCore.ResponseCompression.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Rewrite" ProjectPath="$(RepoRoot)src\Middleware\Rewrite\src\Microsoft.AspNetCore.Rewrite.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Session" ProjectPath="$(RepoRoot)src\Middleware\Session\src\Microsoft.AspNetCore.Session.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SpaServices.Extensions" ProjectPath="$(RepoRoot)src\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SpaServices" ProjectPath="$(RepoRoot)src\Middleware\SpaServices\src\Microsoft.AspNetCore.SpaServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.StaticFiles" ProjectPath="$(RepoRoot)src\Middleware\StaticFiles\src\Microsoft.AspNetCore.StaticFiles.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.WebSockets" ProjectPath="$(RepoRoot)src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Mvc.Razor.Extensions\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor.Language" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Razor.Language\src\Microsoft.AspNetCore.Razor.Language.csproj" />
<ProjectReferenceProvider Include="rzc" ProjectPath="$(RepoRoot)src\Razor\Microsoft.AspNetCore.Razor.Tools\src\rzc.csproj" />
<ProjectReferenceProvider Include="Microsoft.CodeAnalysis.Razor" ProjectPath="$(RepoRoot)src\Razor\Microsoft.CodeAnalysis.Razor\src\Microsoft.CodeAnalysis.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.NET.Sdk.Razor" ProjectPath="$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\Microsoft.NET.Sdk.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor.Runtime" ProjectPath="$(RepoRoot)src\Razor\Razor.Runtime\src\Microsoft.AspNetCore.Razor.Runtime.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor" ProjectPath="$(RepoRoot)src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Abstractions" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Abstractions\src\Microsoft.AspNetCore.Mvc.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ApiExplorer" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ApiExplorer\src\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Core" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Cors" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Cors\src\Microsoft.AspNetCore.Mvc.Cors.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.DataAnnotations" ProjectPath="$(RepoRoot)src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Formatters.Json" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Formatters.Json\src\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Formatters.Xml\src\Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Localization" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Localization\src\Microsoft.AspNetCore.Mvc.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" ProjectPath="$(RepoRoot)src\Mvc\Mvc.NewtonsoftJson\src\Microsoft.AspNetCore.Mvc.NewtonsoftJson.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Razor.RuntimeCompilation\src\Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.RazorPages" ProjectPath="$(RepoRoot)src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.TagHelpers" ProjectPath="$(RepoRoot)src\Mvc\Mvc.TagHelpers\src\Microsoft.AspNetCore.Mvc.TagHelpers.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Testing" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ViewFeatures" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc" ProjectPath="$(RepoRoot)src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" ProjectPath="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureAD.UI\src\Microsoft.AspNetCore.Authentication.AzureAD.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" ProjectPath="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureADB2C.UI\src\Microsoft.AspNetCore.Authentication.AzureADB2C.UI.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.AzureAppServices.HostingStartup" ProjectPath="$(RepoRoot)src\Azure\AzureAppServices.HostingStartup\src\Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.AzureAppServicesIntegration" ProjectPath="$(RepoRoot)src\Azure\AzureAppServicesIntegration\src\Microsoft.AspNetCore.AzureAppServicesIntegration.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Client.Core" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Client.Core\src\Microsoft.AspNetCore.SignalR.Client.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Client" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections.Client" ProjectPath="$(RepoRoot)src\SignalR\clients\csharp\Http.Connections.Client\src\Microsoft.AspNetCore.Http.Connections.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections.Common" ProjectPath="$(RepoRoot)src\SignalR\common\Http.Connections.Common\src\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Http.Connections" ProjectPath="$(RepoRoot)src\SignalR\common\Http.Connections\src\Microsoft.AspNetCore.Http.Connections.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.Json" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.MessagePack\src\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" ProjectPath="$(RepoRoot)src\SignalR\common\Protocols.NewtonsoftJson\src\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Common" ProjectPath="$(RepoRoot)src\SignalR\common\SignalR.Common\src\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Core" ProjectPath="$(RepoRoot)src\SignalR\server\Core\src\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR" ProjectPath="$(RepoRoot)src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.Specification.Tests" ProjectPath="$(RepoRoot)src\SignalR\server\Specification.Tests\src\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" ProjectPath="$(RepoRoot)src\SignalR\server\StackExchangeRedis\src\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Authorization" ProjectPath="$(RepoRoot)src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components" ProjectPath="$(RepoRoot)src\Components\Components\src\Microsoft.AspNetCore.Components.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" />
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web.Extensions" ProjectPath="$(RepoRoot)src\Components\Web.Extensions\src\Microsoft.AspNetCore.Components.Web.Extensions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Authentication.WebAssembly.Msal" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj" />
<ProjectReferenceProvider Include="Microsoft.JSInterop.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\JSInterop\src\Microsoft.JSInterop.WebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.NET.Sdk.BlazorWebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Server" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.FileProviders.Embedded" ProjectPath="$(RepoRoot)src\FileProviders\Embedded\src\Microsoft.Extensions.FileProviders.Embedded.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Configuration.KeyPerFile" ProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\src\Microsoft.Extensions.Configuration.KeyPerFile.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization.Abstractions" ProjectPath="$(RepoRoot)src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization" ProjectPath="$(RepoRoot)src\Localization\Localization\src\Microsoft.Extensions.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ObjectPool" ProjectPath="$(RepoRoot)src\ObjectPool\src\Microsoft.Extensions.ObjectPool.csproj" />
<ProjectReferenceProvider Include="Microsoft.JSInterop" ProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.WebEncoders" ProjectPath="$(RepoRoot)src\WebEncoders\src\Microsoft.Extensions.WebEncoders.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" ProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\src\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\src\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Testing" ProjectPath="$(RepoRoot)src\Testing\src\Microsoft.AspNetCore.Testing.csproj" />
</ItemGroup>
</Project>

View File

@ -1,6 +0,0 @@
<Project>
<!-- Minimize what gets set to avoid useless references in this simple project. -->
<Import Project="..\Common.props" />
<Import Project="..\Versions.props" />
<Import Project="..\Dependencies.props" />
</Project>

View File

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

View File

@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--
Gather project references for compilation against RTM packages. %(RTMVersion) is set for about a dozen packages
in all servicing builds. Cannot reference two versions of a package, mandating this separate package.
-->
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<!-- Don't bother building anything here. We only need to ensure the RTM packages are on disk. -->
<DebugType>none</DebugType>
<IncludeBuildOutput>false</IncludeBuildOutput>
<CopyBuildOutputToPublishDirectory>false</CopyBuildOutputToPublishDirectory>
<CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
<GenerateDependencyFile>false</GenerateDependencyFile>
<!-- This project should not be referenced via the `<Reference>` implementation. -->
<IsProjectReferenceProvider>false</IsProjectReferenceProvider>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="@(LatestPackageReference->HasMetadata('RTMVersion'))" Version="%(RTMVersion)" />
</ItemGroup>
<!-- Arcade SDK calls Test target on every project in the repo but provides an empty fallback. Do same here. -->
<Target Name="Test" />
</Project>

View File

@ -13,304 +13,309 @@
<Uri>https://github.com/dotnet/blazor</Uri>
<Sha>cc449601d638ffaab58ae9487f0fd010bb178a12</Sha>
</Dependency>
<Dependency Name="dotnet-ef" Version="5.0.0-preview.8.20360.8">
<Dependency Name="dotnet-ef" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore" Version="5.0.0-preview.8.20360.8">
<Dependency Name="Microsoft.EntityFrameworkCore" Version="5.0.0-rc.1.20372.13">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>58abc390e0e3eb849b5773da3f5ed2982ade521d</Sha>
<Sha>59734ea22f29d22f8d0c1673c59a99c54ec4e78d</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Configuration" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Hosting" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Logging" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Options" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Internal.Transport" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.Internal.Transport" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Win32.SystemEvents" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Win32.SystemEvents" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.ComponentModel.Annotations" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.ComponentModel.Annotations" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Diagnostics.DiagnosticSource" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Diagnostics.DiagnosticSource" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Diagnostics.EventLog" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Diagnostics.EventLog" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Drawing.Common" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Drawing.Common" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.IO.Pipelines" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.IO.Pipelines" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Net.Http.Json" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Net.Http.Json" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Net.Http.WinHttpHandler" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Net.Http.WinHttpHandler" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Net.WebSockets.WebSocketProtocol" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Net.WebSockets.WebSocketProtocol" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Reflection.Metadata" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Reflection.Metadata" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Resources.Extensions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Resources.Extensions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Cng" Version="5.0.0-preview.8.20361.2">
<!-- System.Security.AccessControl should only be referenced in Dependencies.props and RTMVersions.csproj. -->
<Dependency Name="System.Security.AccessControl" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Pkcs" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Security.Cryptography.Cng" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Xml" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Security.Cryptography.Pkcs" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Security.Permissions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Security.Cryptography.Xml" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Security.Principal.Windows" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Security.Permissions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.ServiceProcess.ServiceController" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Security.Principal.Windows" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Text.Encodings.Web" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.ServiceProcess.ServiceController" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Text.Json" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Text.Encodings.Web" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Threading.Channels" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Text.Json" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="System.Windows.Extensions" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Threading.Channels" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyModel" Version="5.0.0-preview.8.20361.2">
<Dependency Name="System.Windows.Extensions" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.NETCore.App.Ref" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.Extensions.DependencyModel" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.NETCore.App.Ref" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<!--
Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
All Runtime.$rid packages should have the same version.
-->
<Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.NETCore.App.Internal" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.NETCore.App.Internal" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<!-- Listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
<Dependency Name="Microsoft.NETCore.Platforms" Version="5.0.0-preview.8.20361.2">
<Dependency Name="Microsoft.NETCore.Platforms" Version="5.0.0-rc.1.20370.4">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>f37dd6fc8595e130909dcb3085a56342d04aa20c</Sha>
<Sha>0e0e648770e54b12c2fa81a77538ce1a72fca8af</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="5.0.0-beta.20377.2">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="5.0.0-beta.20374.1">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>22d6355c4f3c9ac00b0e3abf9d85f2fb07e4787b</Sha>
<Sha>f6192d1e284a08ac05041d05fa6e60dec74b24f5</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="5.0.0-beta.20364.3">
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="5.0.0-beta.20374.1">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>ff5d4b6c8dbdaeacb6e6159d3f8185118dffd915</Sha>
<Sha>f6192d1e284a08ac05041d05fa6e60dec74b24f5</Sha>
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.8.0-1.20361.1">
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.8.0-2.20379.3">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>f24d2c5c98211908ab90d6f1f42e7592411d6058</Sha>
<Sha>7238563e5c051535770a9266c45d925012ad2b76</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>

View File

@ -9,14 +9,14 @@
<AspNetCoreMajorVersion>5</AspNetCoreMajorVersion>
<AspNetCoreMinorVersion>0</AspNetCoreMinorVersion>
<AspNetCorePatchVersion>0</AspNetCorePatchVersion>
<PreReleaseVersionIteration>8</PreReleaseVersionIteration>
<PreReleaseVersionIteration>1</PreReleaseVersionIteration>
<!--
When StabilizePackageVersion is set to 'true', this branch will produce stable outputs for 'Shipping' packages
-->
<StabilizePackageVersion Condition="'$(StabilizePackageVersion)' == ''">false</StabilizePackageVersion>
<DotNetFinalVersionKind Condition="'$(StabilizePackageVersion)' == 'true'">release</DotNetFinalVersionKind>
<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
<PreReleaseBrandingLabel>Preview $(PreReleaseVersionIteration)</PreReleaseBrandingLabel>
<PreReleaseVersionLabel>rc</PreReleaseVersionLabel>
<PreReleaseBrandingLabel>RC $(PreReleaseVersionIteration)</PreReleaseBrandingLabel>
<IncludePreReleaseLabelInPackageVersion>true</IncludePreReleaseLabelInPackageVersion>
<IncludePreReleaseLabelInPackageVersion Condition=" '$(DotNetFinalVersionKind)' == 'release' ">false</IncludePreReleaseLabelInPackageVersion>
<AspNetCoreMajorMinorVersion>$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion)</AspNetCoreMajorMinorVersion>
@ -62,82 +62,84 @@
-->
<PropertyGroup Label="Automated">
<!-- Packages from dotnet/roslyn -->
<MicrosoftNetCompilersToolsetPackageVersion>3.8.0-1.20361.1</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>3.8.0-2.20379.3</MicrosoftNetCompilersToolsetPackageVersion>
<!-- Packages from dotnet/runtime -->
<MicrosoftExtensionsDependencyModelPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftNETCoreAppInternalPackageVersion>5.0.0-preview.8.20361.2</MicrosoftNETCoreAppInternalPackageVersion>
<MicrosoftNETCoreAppRefPackageVersion>5.0.0-preview.8.20361.2</MicrosoftNETCoreAppRefPackageVersion>
<MicrosoftNETCoreAppRuntimewinx64PackageVersion>5.0.0-preview.8.20361.2</MicrosoftNETCoreAppRuntimewinx64PackageVersion>
<MicrosoftWin32RegistryPackageVersion>5.0.0-preview.8.20361.2</MicrosoftWin32RegistryPackageVersion>
<MicrosoftWin32SystemEventsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftWin32SystemEventsPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsInternalTransportPackageVersion>5.0.0-preview.8.20361.2</MicrosoftExtensionsInternalTransportPackageVersion>
<SystemComponentModelAnnotationsPackageVersion>5.0.0-preview.8.20361.2</SystemComponentModelAnnotationsPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>5.0.0-preview.8.20361.2</SystemDiagnosticsDiagnosticSourcePackageVersion>
<SystemDiagnosticsEventLogPackageVersion>5.0.0-preview.8.20361.2</SystemDiagnosticsEventLogPackageVersion>
<SystemDrawingCommonPackageVersion>5.0.0-preview.8.20361.2</SystemDrawingCommonPackageVersion>
<SystemIOPipelinesPackageVersion>5.0.0-preview.8.20361.2</SystemIOPipelinesPackageVersion>
<SystemNetHttpJsonPackageVersion>5.0.0-preview.8.20361.2</SystemNetHttpJsonPackageVersion>
<SystemNetHttpWinHttpHandlerPackageVersion>5.0.0-preview.8.20361.2</SystemNetHttpWinHttpHandlerPackageVersion>
<SystemNetWebSocketsWebSocketProtocolPackageVersion>5.0.0-preview.8.20361.2</SystemNetWebSocketsWebSocketProtocolPackageVersion>
<SystemReflectionMetadataPackageVersion>5.0.0-preview.8.20361.2</SystemReflectionMetadataPackageVersion>
<SystemResourcesExtensionsPackageVersion>5.0.0-preview.8.20361.2</SystemResourcesExtensionsPackageVersion>
<SystemRuntimeCompilerServicesUnsafePackageVersion>5.0.0-preview.8.20361.2</SystemRuntimeCompilerServicesUnsafePackageVersion>
<SystemSecurityCryptographyCngPackageVersion>5.0.0-preview.8.20361.2</SystemSecurityCryptographyCngPackageVersion>
<SystemSecurityCryptographyPkcsPackageVersion>5.0.0-preview.8.20361.2</SystemSecurityCryptographyPkcsPackageVersion>
<SystemSecurityCryptographyXmlPackageVersion>5.0.0-preview.8.20361.2</SystemSecurityCryptographyXmlPackageVersion>
<SystemSecurityPermissionsPackageVersion>5.0.0-preview.8.20361.2</SystemSecurityPermissionsPackageVersion>
<SystemSecurityPrincipalWindowsPackageVersion>5.0.0-preview.8.20361.2</SystemSecurityPrincipalWindowsPackageVersion>
<SystemServiceProcessServiceControllerPackageVersion>5.0.0-preview.8.20361.2</SystemServiceProcessServiceControllerPackageVersion>
<SystemTextEncodingsWebPackageVersion>5.0.0-preview.8.20361.2</SystemTextEncodingsWebPackageVersion>
<SystemTextJsonPackageVersion>5.0.0-preview.8.20361.2</SystemTextJsonPackageVersion>
<SystemThreadingChannelsPackageVersion>5.0.0-preview.8.20361.2</SystemThreadingChannelsPackageVersion>
<SystemWindowsExtensionsPackageVersion>5.0.0-preview.8.20361.2</SystemWindowsExtensionsPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftNETCoreAppInternalPackageVersion>5.0.0-rc.1.20370.4</MicrosoftNETCoreAppInternalPackageVersion>
<MicrosoftNETCoreAppRefPackageVersion>5.0.0-rc.1.20370.4</MicrosoftNETCoreAppRefPackageVersion>
<MicrosoftNETCoreAppRuntimewinx64PackageVersion>5.0.0-rc.1.20370.4</MicrosoftNETCoreAppRuntimewinx64PackageVersion>
<MicrosoftWin32RegistryPackageVersion>5.0.0-rc.1.20370.4</MicrosoftWin32RegistryPackageVersion>
<MicrosoftWin32SystemEventsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftWin32SystemEventsPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsInternalTransportPackageVersion>5.0.0-rc.1.20370.4</MicrosoftExtensionsInternalTransportPackageVersion>
<SystemComponentModelAnnotationsPackageVersion>5.0.0-rc.1.20370.4</SystemComponentModelAnnotationsPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>5.0.0-rc.1.20370.4</SystemDiagnosticsDiagnosticSourcePackageVersion>
<SystemDiagnosticsEventLogPackageVersion>5.0.0-rc.1.20370.4</SystemDiagnosticsEventLogPackageVersion>
<SystemDrawingCommonPackageVersion>5.0.0-rc.1.20370.4</SystemDrawingCommonPackageVersion>
<SystemIOPipelinesPackageVersion>5.0.0-rc.1.20370.4</SystemIOPipelinesPackageVersion>
<SystemNetHttpJsonPackageVersion>5.0.0-rc.1.20370.4</SystemNetHttpJsonPackageVersion>
<SystemNetHttpWinHttpHandlerPackageVersion>5.0.0-rc.1.20370.4</SystemNetHttpWinHttpHandlerPackageVersion>
<SystemNetWebSocketsWebSocketProtocolPackageVersion>5.0.0-rc.1.20370.4</SystemNetWebSocketsWebSocketProtocolPackageVersion>
<SystemReflectionMetadataPackageVersion>5.0.0-rc.1.20370.4</SystemReflectionMetadataPackageVersion>
<SystemResourcesExtensionsPackageVersion>5.0.0-rc.1.20370.4</SystemResourcesExtensionsPackageVersion>
<SystemRuntimeCompilerServicesUnsafePackageVersion>5.0.0-rc.1.20370.4</SystemRuntimeCompilerServicesUnsafePackageVersion>
<!-- System.Security.AccessControl should only be referenced in Dependencies.props and RTMVersions.csproj. -->
<SystemSecurityAccessControlPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityAccessControlPackageVersion>
<SystemSecurityCryptographyCngPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityCryptographyCngPackageVersion>
<SystemSecurityCryptographyPkcsPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityCryptographyPkcsPackageVersion>
<SystemSecurityCryptographyXmlPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityCryptographyXmlPackageVersion>
<SystemSecurityPermissionsPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityPermissionsPackageVersion>
<SystemSecurityPrincipalWindowsPackageVersion>5.0.0-rc.1.20370.4</SystemSecurityPrincipalWindowsPackageVersion>
<SystemServiceProcessServiceControllerPackageVersion>5.0.0-rc.1.20370.4</SystemServiceProcessServiceControllerPackageVersion>
<SystemTextEncodingsWebPackageVersion>5.0.0-rc.1.20370.4</SystemTextEncodingsWebPackageVersion>
<SystemTextJsonPackageVersion>5.0.0-rc.1.20370.4</SystemTextJsonPackageVersion>
<SystemThreadingChannelsPackageVersion>5.0.0-rc.1.20370.4</SystemThreadingChannelsPackageVersion>
<SystemWindowsExtensionsPackageVersion>5.0.0-rc.1.20370.4</SystemWindowsExtensionsPackageVersion>
<!-- Only listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
<MicrosoftNETCorePlatformsPackageVersion>5.0.0-preview.8.20361.2</MicrosoftNETCorePlatformsPackageVersion>
<MicrosoftNETCorePlatformsPackageVersion>5.0.0-rc.1.20370.4</MicrosoftNETCorePlatformsPackageVersion>
<!-- Packages from dotnet/blazor -->
<MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion>3.2.0</MicrosoftAspNetCoreComponentsWebAssemblyRuntimePackageVersion>
<!-- Packages from dotnet/efcore -->
<dotnetefPackageVersion>5.0.0-preview.8.20360.8</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>5.0.0-preview.8.20360.8</MicrosoftEntityFrameworkCorePackageVersion>
<dotnetefPackageVersion>5.0.0-rc.1.20372.13</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>5.0.0-rc.1.20372.13</MicrosoftEntityFrameworkCorePackageVersion>
</PropertyGroup>
<!--
@ -155,7 +157,6 @@
-->
<MicrosoftNETCoreAppRuntimeVersion>$(MicrosoftNETCoreAppRuntimewinx64PackageVersion)</MicrosoftNETCoreAppRuntimeVersion>
</PropertyGroup>
<!--
We ship ref/ assemblies for runtime packages in our targeting targeting pack. When servicing that targeting pack,
these assemblies must not change. Must also compile our assemblies against the initial ref/ assemblies for runtime
@ -163,6 +164,14 @@
Upshot is we need Major.Minor.0 runtime packages for compilation and the targeting pack and Major.Minor.Latest
runtime packages for everything else. This is not an issue for assemblies available in Microsoft.NETCore.App.Ref or
Microsoft.Extensions.Internal.Transport because it is next to impossible we would service those packages.
System.Security.AccessControl should only be referenced in Dependencies.props and RTMVersions.csproj. Because
it's a transitive reference, we reship the ref/ assembly in Microsoft.AspNetCore.App.Ref. dotnet/runtime ships
the implementation assemblies in Microsoft.NETCore.App.Runtime.* packages.
If testing this configuration prior to servicing, update the versions of dependencies too. E.g. change
`$(SystemSecurityPrincipalWindowsV0PackageVersion)` if you change `$(SystemSecurityAccessControlV0PackageVersion)`
because System.Security.AccessControl will otherwise be loadable. This should not be necessary in servicing.
-->
<PropertyGroup Condition=" '$(IsServicingBuild)' == 'true' ">
<MicrosoftWin32RegistryV0PackageVersion>$(MicrosoftWin32RegistryPackageVersion.Split('.')[0]).$(MicrosoftWin32RegistryPackageVersion.Split('.')[1]).0</MicrosoftWin32RegistryV0PackageVersion>
@ -178,7 +187,6 @@
<SystemSecurityPrincipalWindowsV0PackageVersion>$(SystemSecurityPrincipalWindowsPackageVersion.Split('.')[0]).$(SystemSecurityPrincipalWindowsPackageVersion.Split('.')[1]).0</SystemSecurityPrincipalWindowsV0PackageVersion>
<SystemWindowsExtensionsV0PackageVersion>$(SystemWindowsExtensionsPackageVersion.Split('.')[0]).$(SystemWindowsExtensionsPackageVersion.Split('.')[1]).0</SystemWindowsExtensionsV0PackageVersion>
</PropertyGroup>
<PropertyGroup Label="Manual">
<!-- DiagnosticAdapter package pinned temporarily until migrated/deprecated -->
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>5.0.0-preview.4.20180.4</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
@ -211,6 +219,7 @@
<MicrosoftCodeAnalysisCommonPackageVersion>3.7.0-4.20351.7</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>3.7.0-4.20351.7</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>3.7.0-4.20351.7</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisPublicApiAnalyzersPackageVersion>3.3.0-beta2.final</MicrosoftCodeAnalysisPublicApiAnalyzersPackageVersion>
<MicrosoftCodeAnalysisFxCopAnalyzersPackageVersion>3.0.0</MicrosoftCodeAnalysisFxCopAnalyzersPackageVersion>
<MicrosoftCssParserPackageVersion>1.0.0-20200708.1</MicrosoftCssParserPackageVersion>
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.19.8</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
@ -255,9 +264,6 @@
<MonoCecilPackageVersion>0.11.2</MonoCecilPackageVersion>
<NewtonsoftJsonBsonPackageVersion>1.0.2</NewtonsoftJsonBsonPackageVersion>
<NewtonsoftJsonPackageVersion>12.0.2</NewtonsoftJsonPackageVersion>
<!-- Begin: STOP!!! Razor need to reference the version of JSON that our HOSTS support. -->
<Razor_NewtonsoftJsonPackageVersion>9.0.1</Razor_NewtonsoftJsonPackageVersion>
<!-- End: STOP!!! Razor need to reference the version of JSON that our HOSTS support. -->
<NSwagApiDescriptionClientPackageVersion>13.0.4</NSwagApiDescriptionClientPackageVersion>
<SeleniumSupportPackageVersion>3.12.1</SeleniumSupportPackageVersion>
<SeleniumWebDriverMicrosoftDriverPackageVersion>17.17134.0</SeleniumWebDriverMicrosoftDriverPackageVersion>

View File

@ -127,29 +127,40 @@ endif()
# Specify link flags
function(add_toolchain_linker_flag Flag)
set(Config "${ARGV1}")
set(CONFIG_SUFFIX "")
if (NOT Config STREQUAL "")
set(CONFIG_SUFFIX "_${Config}")
endif()
set("CMAKE_EXE_LINKER_FLAGS${CONFIG_SUFFIX}" "${CMAKE_EXE_LINKER_FLAGS${CONFIG_SUFFIX}} ${Flag}" PARENT_SCOPE)
set("CMAKE_SHARED_LINKER_FLAGS${CONFIG_SUFFIX}" "${CMAKE_SHARED_LINKER_FLAGS${CONFIG_SUFFIX}} ${Flag}" PARENT_SCOPE)
endfunction()
if(TARGET_ARCH_NAME STREQUAL "armel")
if(DEFINED TIZEN_TOOLCHAIN) # For Tizen only
add_link_options("-B${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}")
add_link_options("-L${CROSS_ROOTFS}/lib")
add_link_options("-L${CROSS_ROOTFS}/usr/lib")
add_link_options("-L${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}")
add_toolchain_linker_flag("-B${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}")
endif()
elseif(TARGET_ARCH_NAME STREQUAL "arm64")
if(DEFINED TIZEN_TOOLCHAIN) # For Tizen only
add_link_options("-B${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
add_link_options("-L${CROSS_ROOTFS}/lib64")
add_link_options("-L${CROSS_ROOTFS}/usr/lib64")
add_link_options("-L${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
add_toolchain_linker_flag("-B${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib64")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib64")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
add_link_options("-Wl,--rpath-link=${CROSS_ROOTFS}/lib64")
add_link_options("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64")
add_link_options("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/lib64")
add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64")
add_toolchain_linker_flag("-Wl,--rpath-link=${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
endif()
elseif(TARGET_ARCH_NAME STREQUAL "x86")
add_link_options(-m32)
add_toolchain_linker_flag(-m32)
elseif(ILLUMOS)
add_link_options("-L${CROSS_ROOTFS}/lib/amd64")
add_link_options("-L${CROSS_ROOTFS}/usr/amd64/lib")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/lib/amd64")
add_toolchain_linker_flag("-L${CROSS_ROOTFS}/usr/amd64/lib")
endif()
# Specify compile options

View File

@ -247,7 +247,7 @@ namespace RunTests
{
// Timeout test run 5 minutes before the Helix job would timeout
var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5)));
var commonTestArgs = $"vstest {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame";
var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\"";
if (Options.Quarantined)
{
Console.WriteLine("Running quarantined tests.");
@ -331,6 +331,22 @@ namespace RunTests
{
Console.WriteLine("No logs found in artifacts/log");
}
Console.WriteLine($"Copying TestResults/**/*.dmp to {HELIX_WORKITEM_UPLOAD_ROOT}/");
if (Directory.Exists("TestResults"))
{
foreach (var file in Directory.EnumerateFiles("TestResults", "*.dmp", SearchOption.AllDirectories))
{
var fileName = Path.GetFileName(file);
Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}");
// Need to copy to HELIX_WORKITEM_UPLOAD_ROOT and HELIX_WORKITEM_UPLOAD_ROOT/../ in order for Azure Devops attachments to link properly and for Helix to store the logs
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName));
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "..", fileName));
}
}
else
{
Console.WriteLine("No dmps found in TestResults");
}
}
}
}

View File

@ -15,7 +15,6 @@
Windows.7.Amd64.Open
OSX.1014.Amd64.Open
Centos.7.Amd64.Open
Debian.8.Amd64.Open
Debian.9.Amd64.Open
Redhat.7.Amd64.Open
.PARAMETER RunQuarantinedTests
@ -39,4 +38,4 @@ $env:BUILD_REPOSITORY_NAME="aspnetcore"
$env:SYSTEM_TEAMPROJECT="aspnetcore"
$HelixQueues = $HelixQueues -replace ";", "%3B"
dotnet msbuild $Project /t:Helix /p:TargetArchitecture="$TargetArchitecture" /p:IsRequiredCheck=true /p:IsHelixDaily=true /p:HelixTargetQueues=$HelixQueues /p:RunQuarantinedTests=$RunQuarantinedTests /p:_UseHelixOpenQueues=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log
dotnet msbuild $Project /t:Helix /p:TargetArchitecture="$TargetArchitecture" /p:IsRequiredCheck=true /p:IsHelixDaily=true /p:HelixTargetQueues=$HelixQueues /p:RunQuarantinedTests=$RunQuarantinedTests /p:_UseHelixOpenQueues=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log

View File

@ -18,9 +18,8 @@
several versions older than latest. We reference the project to ensure it's built before the other projects that use it. Since this
is a project reference, we must explicitly import the props file and also specify the output location of the SDK directory.
-->
<ProjectReference Include="$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\Microsoft.NET.Sdk.Razor.csproj"
<Reference Include="Microsoft.NET.Sdk.Razor"
PrivateAssets="All"
IsImplicitlyDefined="true"
ReferenceOutputAssembly="false"
SkipGetTargetFrameworkProperties="true"
UndefineProperties="TargetFramework;TargetFrameworks" />

View File

@ -6,6 +6,30 @@
<TargetFrameworkIdentifier>.NETFramework</TargetFrameworkIdentifier>
</PropertyGroup>
<!-- Ensure API changes show up clearly in PRs. -->
<PropertyGroup>
<_TFMDirectory>$(TargetFramework)</_TFMDirectory>
<_TFMDirectory Condition=" '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' ">netcoreapp</_TFMDirectory>
<!-- Public members should not use oblivious types. Not done with all nullable annotations. -->
<NoWarn>$(NoWarn);RS0041</NoWarn>
</PropertyGroup>
<ItemGroup Condition=" '$(IsImplementationProject)' == 'true' AND
'$(DotNetBuildFromSource)' != 'true' AND
! $(RepoRelativeProjectDir.Contains('Tools')) ">
<!-- Package does nothing in projects lacking PublicAPI.Shipped.txt or PublicAPI.Unshipped.txt files. -->
<Reference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" ExcludeAssets="Compile" PrivateAssets="All" />
<AdditionalFiles Include="PublicAPI.Shipped.txt"
Condition=" Exists('$(MSBuildProjectDirectory)\PublicAPI.Shipped.txt') " />
<AdditionalFiles Include="PublicAPI.Unshipped.txt"
Condition=" Exists('$(MSBuildProjectDirectory)\PublicAPI.Unshipped.txt') " />
<AdditionalFiles Include="PublicAPI.Shipped.txt"
Condition=" Exists('$(MSBuildProjectDirectory)\$(_TFMDirectory)\PublicAPI.Shipped.txt') " />
<AdditionalFiles Include="PublicAPI.Unshipped.txt"
Condition=" Exists('$(MSBuildProjectDirectory)\$(_TFMDirectory)\PublicAPI.Unshipped.txt') " />
</ItemGroup>
<Target Name="GetCustomAssemblyAttributes"
BeforeTargets="GetAssemblyAttributes"
DependsOnTargets="InitializeSourceControlInformation">

View File

@ -32,7 +32,6 @@
<HelixAvailableTargetQueue Include="Ubuntu.1804.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="Ubuntu.2004.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="Centos.7.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="Debian.8.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="Debian.9.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="Redhat.7.Amd64.Open" Platform="Linux" />
<HelixAvailableTargetQueue Include="(Fedora.28.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-28-helix-09ca40b-20190508143249" Platform="Linux" />

View File

@ -1,5 +1,4 @@
<!--
The targets in this file are used to implement custom <Reference> resolution.
For more details, see /docs/ReferenceResolution.md.
@ -76,8 +75,6 @@
<_AllowedExplicitPackageReference Include="FSharp.Core" Condition="'$(MSBuildProjectExtension)' == '.fsproj'" />
<_ExplicitPackageReference Include="@(PackageReference)" Exclude="@(_ImplicitPackageReference);@(_AllowedExplicitPackageReference)" />
<_UnusedProjectReferenceProvider Include="@(ProjectReferenceProvider)" Exclude="@(Reference)" />
<_CompilationOnlyReference Condition="'$(TargetFramework)' == 'netstandard2.0'"
Include="@(Reference->WithMetadataValue('NuGetPackageId','NETStandard.Library'))" />
@ -91,24 +88,55 @@
@(Reference->WithMetadataValue('IsSharedSource', 'true'));
@(Reference->WithMetadataValue('PrivateAssets', 'All'))" />
<_OriginalReferences Include="@(Reference)" />
<!--
Turn Reference items into a ProjectReference when UseProjectReferences is true.
Order matters. This comes before package resolution because projects should be used when possible instead of packages.
-->
<_ProjectReferenceByAssemblyName Condition="'$(UseProjectReferences)' == 'true'"
Include="@(ProjectReferenceProvider)"
Exclude="@(_UnusedProjectReferenceProvider)" />
<ProjectReference Include="@(_ProjectReferenceByAssemblyName->'%(ProjectPath)')" />
<Reference Remove="@(_ProjectReferenceByAssemblyName)" />
</ItemGroup>
<!--
This target resolves remaining Referene items to Packages, if possible. If not, they are left as Reference items fo the SDK to resolve.
This target helps ensure projects within the shared framework do no unintentionally add new references,
and that assemblies outside the shared framework reference the framework as a whole instead of using
individual assemblies.
Turn Reference items into a ProjectReference when UseProjectReferences is true. Order matters; this
comes before package resolution because projects should be used when possible instead of packages.
-->
<ItemGroup Condition=" '$(EnableCustomReferenceResolution)' == 'true' AND '$(UseProjectReferences)' == 'true' ">
<!-- Copy then Update / Copy to intersect the ProjectReferenceProvider and Reference item groups. -->
<_AllProjectReference Include="@(ProjectReferenceProvider)" />
<!-- Use only Reference items when project is a reference provider. Simplifies project moves and shortens files. -->
<_AllProjectReference Update="@(ProjectReference->'%(Filename)')" DirectUse="1" />
<_AllProjectReference Update="@(Reference)" Use="1">
<!--
Metadata list is long because (a) Update defaults to copying no metadata and (b) ProjectReference metadata
may include (real) Reference metadata and MSBuild task parameters. Even so, the list below is not exhaustive.
-->
<Aliases>%(Reference.Aliases)</Aliases>
<BuildInParallel>%(Reference.BuildInParallel)</BuildInParallel>
<DoNotHarvest>%(Reference.DoNotHarvest)</DoNotHarvest>
<ExcludeAssets>%(Reference.ExcludeAssets)</ExcludeAssets>
<IncludeAssets>%(Reference.IncludeAssets)</IncludeAssets>
<IsNativeImage>%(Reference.IsNativeImage)</IsNativeImage>
<LinkBase>%(Reference.LinkBase)</LinkBase>
<Name>%(Reference.Name)</Name>
<OutputItemType>%(Reference.OutputItemType)</OutputItemType>
<Package>%(Reference.Package)</Package>
<Private>%(Reference.Private)</Private>
<PrivateAssets>%(Reference.PrivateAssets)</PrivateAssets>
<Project>%(Reference.Project)</Project>
<Properties>%(Reference.Properties)</Properties>
<Publish>%(Reference.Publish)</Publish>
<ReferenceOutputAssembly>%(Reference.ReferenceOutputAssembly)</ReferenceOutputAssembly>
<SetPlatform>%(Reference.SetPlatform)</SetPlatform>
<SkipGetTargetFrameworkProperties>%(Reference.SkipGetTargetFrameworkProperties)</SkipGetTargetFrameworkProperties>
<Targets>%(Reference.Targets)</Targets>
<UndefineProperties>%(Reference.UndefineProperties)</UndefineProperties>
<Watch>%(Reference.Watch)</Watch>
</_AllProjectReference>
<ProjectReference Include="@(_AllProjectReference->WithMetadataValue('Use', '1')->'%(ProjectPath)')" Use="" />
<Reference Remove="@(_AllProjectReference->WithMetadataValue('Use', '1'))" />
</ItemGroup>
<!--
This target helps ensure projects within the shared framework do no unintentionally add new references, and that
assemblies outside the shared framework reference the framework as a whole instead of using individual assemblies.
In addition, enforce use of Reference items for projects reference providers.
-->
<Target Name="_CheckForReferenceBoundaries" BeforeTargets="CollectPackageReferences;ResolveReferences">
<Error
@ -118,6 +146,10 @@
<Error
Condition="@(_InvalidReferenceToNonSharedFxAssembly->Count()) != 0 AND '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'"
Text="Cannot reference &quot;%(_InvalidReferenceToNonSharedFxAssembly.Identity)&quot;. This dependency is not in the shared framework. See docs/SharedFramework.md for instructions on how to modify what is in the shared framework." />
<Error
Condition=" '$(EnableCustomReferenceResolution)' == 'true' AND @(_AllProjectReference->WithMetadataValue('DirectUse', '1')->Count()) != 0 "
Text="Cannot reference &quot;%(_AllProjectReference.Identity)&quot; with a ProjectReference item; use a Reference item." />
</Target>
<Target Name="_WarnAboutRedundantRef" AfterTargets="ResolveFrameworkReferences;ProcessFrameworkReferences">
@ -127,14 +159,15 @@
</Target>
<!--
This target resolves remaining Referene items to Packages, if possible. If not, they are left as Reference items fo the SDK to resolve.
This executes on NuGet restore and during DesignTimeBuild. It should not run in the outer, cross-targeting build.
This target resolves remaining Reference items to Packages, if possible. If not, they are left as Reference
items for the SDK to resolve. This executes on NuGet restore and during DesignTimeBuild. It should not run in
outer, cross-targeting build.
-->
<Target Name="ResolveCustomReferences"
BeforeTargets="CheckForImplicitPackageReferenceOverrides;CollectPackageReferences;ResolvePackageAssets"
Condition=" '$(TargetFramework)' != '' AND '$(EnableCustomReferenceResolution)' == 'true' ">
<ItemGroup>
<!-- Ensure only content asset are consumed from .Sources packages -->
<!-- Ensure only content assets are consumed from .Sources packages. -->
<Reference>
<IncludeAssets Condition="'%(IsSharedSource)' == 'true'">ContentFiles;Build</IncludeAssets>
<PrivateAssets Condition="'%(IsSharedSource)' == 'true'">All</PrivateAssets>
@ -205,21 +238,23 @@
</Target>
<!--
Muck with @(ResolvedCompileFileDefinitions) items between generation and use in order to compile against RTM lib/
Change @(ResolvedCompileFileDefinitions) items between generation and use in order to compile against RTM lib/
or ref/ assemblies. The approach works for all TFMs because it happens after a specific assembly is chosen for
compilation; no need to restrict this to (say) the default TFM.
Condition checks for RTMVersions.csproj.nuget.g.props file only to ensure restore operation is complete.
This target could get confused if the layout changes for one of the dozen special-cased packages during servicing.
E.g. ResolvePackageAssets picks a compatible assembly from the 5.0.1 package and _UseRTMReferenceAssemblies
changes the path to one not present in the 5.0.0 package. Fortunately, this will break the build with complaints
about the "invalid strong name".
-->
<Target Name="_UseRTMReferenceAssemblies"
Condition=" '$(EnableCustomReferenceResolution)' == 'true' AND EXISTS('$(RepoRoot)eng\RTMVersions\obj\RTMVersions.csproj.nuget.g.props') "
Condition=" '$(MSBuildProjectName)' != 'RepoTasks' "
AfterTargets="ResolvePackageAssets"
BeforeTargets="GenerateBuildDependencyFile;GeneratePublishDependencyFile;ILLink;ResolveLockFileReferences"
DependsOnTargets="ResolvePackageAssets">
<Error Condition=" !EXISTS('$(RepoRoot)artifacts\obj\RepoTasks\RepoTasks.csproj.nuget.g.props') "
Text="The eng/tools/RepoTasks project must be restored before building other projects." />
<JoinItems Left="@(ResolvedCompileFileDefinitions)"
Right="@(LatestPackageReference->HasMetadata('RTMVersion'))"
LeftKey="Filename"
@ -283,6 +318,8 @@
Text="Only implementation projects should set IsAspNetCoreApp=true." />
<Error Condition=" '$(IsAspNetCoreApp)' != 'true' AND $(HasReferenceAssembly) "
Text="Only projects in the shared framework i.e. IsAspNetCoreApp==true should produce a reference assembly." />
<Warning Condition=" '$(IsProjectReferenceProvider)' == 'true' AND '$(AssemblyName)' != '$(MSBuildProjectName)' "
Text="Project name &quot;$(MSBuildProjectName)&quot; is confusing; assembly is named &quot;$(AssemblyName)&quot;." />
<ItemGroup Condition=" '$(IsProjectReferenceProvider)' == 'true' ">
<ProvidesReference Include="$(AssemblyName)">

View File

@ -100,7 +100,7 @@ namespace RepoTasks
Log.LogMessage(MessageImportance.High, $"Attempting download '{source}' to '{target}'");
using (var httpClient = new HttpClient())
using (var httpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(5) })
{
for (int retryNumber = 0; retryNumber < MaxRetries; retryNumber++)
{
@ -146,4 +146,4 @@ namespace RepoTasks
return null;
}
}
}
}

View File

@ -36,5 +36,15 @@
<Reference Include="Microsoft.Deployment.WindowsInstaller.Package">
<HintPath>$(WiXSdkPath)\Microsoft.Deployment.WindowsInstaller.Package.dll</HintPath>
</Reference>
<!--
Gather project references for compilation against RTM packages. %(RTMVersion) is set for about a dozen packages
in all servicing builds. Cannot reference two versions of a package, mandating this separation from projects
using the relevant packages.
-->
<PackageReference Include="@(LatestPackageReference->HasMetadata('RTMVersion'))"
IncludeAssets="None"
PrivateAssets="All"
Version="%(RTMVersion)" />
</ItemGroup>
</Project>

View File

@ -30,7 +30,7 @@
},
"msbuild-sdks": {
"Yarn.MSBuild": "1.15.2",
"Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20377.2",
"Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20364.3"
"Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20374.1",
"Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20374.1"
}
}

View File

@ -34,7 +34,7 @@ namespace A.Internal.Namespace
[Theory]
[MemberData(nameof(PublicMemberDefinitions))]
[QuarantinedTest]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/22440")]
public async Task PublicExposureOfPubternalTypeProducesPUB0001(string member)
{
var code = GetSourceFromNamespaceDeclaration($@"

View File

@ -1,8 +1,10 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace IISSample
@ -58,18 +60,22 @@ namespace IISSample
});
}
public static void Main(string[] args)
public static Task Main(string[] args)
{
var host = new WebHostBuilder()
var host = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseKestrel()
.UseStartup<Startup>();
})
.ConfigureLogging(factory =>
{
factory.AddConsole();
})
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
return host.RunAsync();
}
}
}

View File

@ -1,9 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace IISSample
@ -70,20 +72,23 @@ namespace IISSample
});
}
public static void Main(string[] args)
public static Task Main(string[] args)
{
var host = new WebHostBuilder()
var host = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseKestrel()
.UseAzureAppServices()
.UseStartup<Startup>();
})
.ConfigureLogging(factory =>
{
factory.AddConsole();
})
.UseKestrel()
.UseAzureAppServices()
.UseStartup<Startup>()
.Build();
host.Run();
return host.RunAsync();
}
}
}

View File

@ -54,6 +54,12 @@ namespace Microsoft.AspNetCore.Components.Authorization
[Parameter]
public RenderFragment Authorizing { get; set; }
/// <summary>
/// The resource to which access is being controlled.
/// </summary>
[Parameter]
public object Resource { get; set; }
[CascadingParameter]
private Task<AuthenticationState> ExistingCascadedAuthenticationState { get; set; }
@ -82,6 +88,7 @@ namespace Microsoft.AspNetCore.Components.Authorization
builder.AddAttribute(2, nameof(AuthorizeRouteViewCore.Authorized), _renderAuthorizedDelegate);
builder.AddAttribute(3, nameof(AuthorizeRouteViewCore.Authorizing), _renderAuthorizingDelegate);
builder.AddAttribute(4, nameof(AuthorizeRouteViewCore.NotAuthorized), _renderNotAuthorizedDelegate);
builder.AddAttribute(5, nameof(AuthorizeRouteViewCore.Resource), Resource);
builder.CloseComponent();
}

View File

@ -76,6 +76,78 @@ namespace Microsoft.AspNetCore.Components.Authorization
edit => AssertPrependText(batch, edit, "Hello from the page with message: Hello, world!"));
}
[Fact]
public void AuthorizesWhenResourceIsSet()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), new Dictionary<string, object>
{
{ nameof(TestPageRequiringAuthorization.Message), "Hello, world!" }
});
var resource = "foo";
_testAuthorizationService.NextResult = AuthorizationResult.Success();
// Act
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
{ nameof(AuthorizeRouteView.Resource), resource }
}));
// Assert: renders layout
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Component<TestPageRequiringAuthorization>(batch.ReferenceFrames[edit.ReferenceFrameIndex]);
},
edit => AssertPrependText(batch, edit, "Layout ends here"));
// Assert: renders page
var pageDiff = batch.GetComponentDiffs<TestPageRequiringAuthorization>().Single();
Assert.Collection(pageDiff.Edits,
edit => AssertPrependText(batch, edit, "Hello from the page with message: Hello, world!"));
// Assert: Asserts that the Resource is present and set to "foo"
Assert.Collection(_testAuthorizationService.AuthorizeCalls, call=>
{
Assert.Equal(resource, call.resource.ToString());
});
}
[Fact]
public void NotAuthorizedWhenResourceMissing()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
_testAuthorizationService.NextResult = AuthorizationResult.Failed();
// Act
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
}));
// Assert: renders layout containing "not authorized" message
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit => AssertPrependText(batch, edit, "Not authorized"),
edit => AssertPrependText(batch, edit, "Layout ends here"));
// Assert: Asserts that the Resource is Null
Assert.Collection(_testAuthorizationService.AuthorizeCalls, call=>
{
Assert.Null(call.resource);
});
}
[Fact]
public void WhenNotAuthorized_RendersDefaultNotAuthorizedContentInsideLayout()
{

View File

@ -61,6 +61,13 @@ namespace Microsoft.AspNetCore.Components
return Receiver.HandleEventAsync(new EventCallbackWorkItem(Delegate), arg);
}
/// <summary>
/// Invokes the delegate associated with this binding and dispatches an event notification to the
/// appropriate component.
/// </summary>
/// <returns>A <see cref="Task"/> which completes asynchronously once event processing has completed.</returns>
public Task InvokeAsync() => InvokeAsync(null!);
object? IEventCallback.UnpackForRenderTree()
{
return RequiresExplicitReceiver ? (object)this : Delegate;

View File

@ -56,6 +56,13 @@ namespace Microsoft.AspNetCore.Components
return Receiver.HandleEventAsync(new EventCallbackWorkItem(Delegate), arg);
}
/// <summary>
/// Invokes the delegate associated with this binding and dispatches an event notification to the
/// appropriate component.
/// </summary>
/// <returns>A <see cref="Task"/> which completes asynchronously once event processing has completed.</returns>
public Task InvokeAsync() => InvokeAsync(default!);
internal EventCallback AsUntyped()
{
return new EventCallback(Receiver ?? Delegate?.Target as IHandleEvent, Delegate);

View File

@ -10,7 +10,6 @@
<ItemGroup>
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" />
<Compile Include="$(ComponentsSharedSourceRoot)src\WebAssemblyJSInteropInternalCalls.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -142,8 +142,8 @@ namespace Microsoft.AspNetCore.Components
var oldIndex = oldParameters._ownerIndex;
var newIndex = _ownerIndex;
var oldEndIndexExcl = oldIndex + oldParameters._frames[oldIndex].ComponentSubtreeLength;
var newEndIndexExcl = newIndex + _frames[newIndex].ComponentSubtreeLength;
var oldEndIndexExcl = oldIndex + oldParameters._frames[oldIndex].ComponentSubtreeLengthField;
var newEndIndexExcl = newIndex + _frames[newIndex].ComponentSubtreeLengthField;
while (true)
{
// First, stop if we've reached the end of either subtree
@ -162,21 +162,21 @@ namespace Microsoft.AspNetCore.Components
ref var newFrame = ref _frames[newIndex];
// Stop if we've reached the end of either subtree's sequence of attributes
oldFinished = oldFrame.FrameType != RenderTreeFrameType.Attribute;
newFinished = newFrame.FrameType != RenderTreeFrameType.Attribute;
oldFinished = oldFrame.FrameTypeField != RenderTreeFrameType.Attribute;
newFinished = newFrame.FrameTypeField != RenderTreeFrameType.Attribute;
if (oldFinished || newFinished)
{
return oldFinished == newFinished; // Same only if we have same number of parameters
}
else
{
if (!string.Equals(oldFrame.AttributeName, newFrame.AttributeName, StringComparison.Ordinal))
if (!string.Equals(oldFrame.AttributeNameField, newFrame.AttributeNameField, StringComparison.Ordinal))
{
return false; // Different names
}
var oldValue = oldFrame.AttributeValue;
var newValue = newFrame.AttributeValue;
var oldValue = oldFrame.AttributeValueField;
var newValue = newFrame.AttributeValueField;
if (ChangeDetection.MayHaveChanged(oldValue, newValue))
{
return false;
@ -216,8 +216,8 @@ namespace Microsoft.AspNetCore.Components
public static ParameterView FromDictionary(IDictionary<string, object> parameters)
{
var frames = new RenderTreeFrame[parameters.Count + 1];
frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName)
.WithElementSubtreeLength(frames.Length);
frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName);
frames[0].ElementSubtreeLengthField = frames.Length;
var i = 0;
foreach (var kvp in parameters)
@ -303,7 +303,7 @@ namespace Microsoft.AspNetCore.Components
{
_frames = frames;
_ownerIndex = ownerIndex;
_ownerDescendantsEndIndexExcl = ownerIndex + _frames[ownerIndex].ElementSubtreeLength;
_ownerDescendantsEndIndexExcl = ownerIndex + _frames[ownerIndex].ElementSubtreeLengthField;
_currentIndex = ownerIndex;
_current = default;
}
@ -321,7 +321,7 @@ namespace Microsoft.AspNetCore.Components
// ... or if you get to its first non-attribute descendant (because attributes
// are always before any other type of descendant)
if (_frames[nextIndex].FrameType != RenderTreeFrameType.Attribute)
if (_frames[nextIndex].FrameTypeField != RenderTreeFrameType.Attribute)
{
return false;
}
@ -329,7 +329,7 @@ namespace Microsoft.AspNetCore.Components
_currentIndex = nextIndex;
ref var frame = ref _frames[_currentIndex];
_current = new ParameterValue(frame.AttributeName, frame.AttributeValue, false);
_current = new ParameterValue(frame.AttributeNameField, frame.AttributeValueField, false);
return true;
}

View File

@ -1,23 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Components.Profiling
{
internal abstract class ComponentsProfiling
{
// For now, this is only intended for use on Blazor WebAssembly, and will have no effect
// when running on Blazor Server. The reason for having the ComponentsProfiling abstraction
// is so that if we later have two different implementations (one for WebAssembly, one for
// Server), the execution characteristics of calling Start/End will be unchanged and historical
// perf data will still be comparable to newer data.
public static readonly ComponentsProfiling Instance = PlatformInfo.IsWebAssembly
? new WebAssemblyComponentsProfiling()
: (ComponentsProfiling)new NoOpComponentsProfiling();
public abstract void Start([CallerMemberName] string? name = null);
public abstract void End([CallerMemberName] string? name = null);
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Profiling
{
internal class NoOpComponentsProfiling : ComponentsProfiling
{
public override void Start(string? name)
{
}
public override void End(string? name)
{
}
}
}

View File

@ -1,41 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using WebAssembly.JSInterop;
namespace Microsoft.AspNetCore.Components.Profiling
{
// Later on, we will likely want to move this into the WebAssembly package. However it needs to
// be inlined into the Components package directly until we're ready to make the underlying
// ComponentsProfile abstraction into a public API. It's possible that this API will never become
// public, or that it will be replaced by something more standard for .NET, if it's possible to
// make that work performantly on WebAssembly.
internal class WebAssemblyComponentsProfiling : ComponentsProfiling
{
static bool IsCapturing = false;
public static void SetCapturing(bool isCapturing)
{
IsCapturing = isCapturing;
}
public override void Start(string? name)
{
if (IsCapturing)
{
InternalCalls.InvokeJSUnmarshalled<string, object, object, object>(
out _, "_blazorProfileStart", name, null, null);
}
}
public override void End(string? name)
{
if (IsCapturing)
{
InternalCalls.InvokeJSUnmarshalled<string, object, object, object>(
out _, "_blazorProfileEnd", name, null, null);
}
}
}
}

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
foreach (var parameter in parameters)
{
var parameterName = parameter.Name;
if (!writers.WritersByName.TryGetValue(parameterName, out var writer))
if (!writers.TryGetValue(parameterName, out var writer))
{
// Case 1: There is nowhere to put this value.
ThrowForUnknownIncomingParameterName(targetType, parameterName);
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
isCaptureUnmatchedValuesParameterSetExplicitly = true;
}
if (writers.WritersByName.TryGetValue(parameterName, out var writer))
if (writers.TryGetValue(parameterName, out var writer))
{
if (!writer.Cascading && parameter.Cascading)
{
@ -245,9 +245,15 @@ namespace Microsoft.AspNetCore.Components.Reflection
private class WritersForType
{
private const int MaxCachedWriterLookups = 100;
private readonly Dictionary<string, IPropertySetter> _underlyingWriters;
private readonly ConcurrentDictionary<string, IPropertySetter?> _referenceEqualityWritersCache;
public WritersForType(Type targetType)
{
WritersByName = new Dictionary<string, IPropertySetter>(StringComparer.OrdinalIgnoreCase);
_underlyingWriters = new Dictionary<string, IPropertySetter>(StringComparer.OrdinalIgnoreCase);
_referenceEqualityWritersCache = new ConcurrentDictionary<string, IPropertySetter?>(ReferenceEqualityComparer.Instance);
foreach (var propertyInfo in GetCandidateBindableProperties(targetType))
{
var parameterAttribute = propertyInfo.GetCustomAttribute<ParameterAttribute>();
@ -267,14 +273,14 @@ namespace Microsoft.AspNetCore.Components.Reflection
var propertySetter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo, cascading: cascadingParameterAttribute != null);
if (WritersByName.ContainsKey(propertyName))
if (_underlyingWriters.ContainsKey(propertyName))
{
throw new InvalidOperationException(
$"The type '{targetType.FullName}' declares more than one parameter matching the " +
$"name '{propertyName.ToLowerInvariant()}'. Parameter names are case-insensitive and must be unique.");
}
WritersByName.Add(propertyName, propertySetter);
_underlyingWriters.Add(propertyName, propertySetter);
if (parameterAttribute != null && parameterAttribute.CaptureUnmatchedValues)
{
@ -298,11 +304,38 @@ namespace Microsoft.AspNetCore.Components.Reflection
}
}
public Dictionary<string, IPropertySetter> WritersByName { get; }
public IPropertySetter? CaptureUnmatchedValuesWriter { get; }
public string? CaptureUnmatchedValuesPropertyName { get; }
public bool TryGetValue(string parameterName, [MaybeNullWhen(false)] out IPropertySetter writer)
{
// In intensive parameter-passing scenarios, one of the most expensive things we do is the
// lookup from parameterName to writer. Pre-5.0 that was because of the string hashing.
// To optimize this, we now have a cache in front of the lookup which is keyed by parameterName's
// object identity (not its string hash). So in most cases we can resolve the lookup without
// having to hash the string. We only fall back on hashing the string if the cache gets full,
// which would only be in very unusual situations because components don't typically have many
// parameters, and the parameterName strings usually come from compile-time constants.
if (!_referenceEqualityWritersCache.TryGetValue(parameterName, out writer))
{
_underlyingWriters.TryGetValue(parameterName, out writer);
// Note that because we're not locking around this, it's possible we might
// actually write more than MaxCachedWriterLookups entries due to concurrent
// writes. However this won't cause any problems.
// Also note that the value we're caching might be 'null'. It's valid to cache
// lookup misses just as much as hits, since then we can more quickly identify
// incoming values that don't have a corresponding writer and thus will end up
// being passed as catch-all parameter values.
if (_referenceEqualityWritersCache.Count < MaxCachedWriterLookups)
{
_referenceEqualityWritersCache.TryAdd(parameterName, writer);
}
}
return writer != null;
}
}
}
}

View File

@ -7,7 +7,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components.RenderTree
@ -28,7 +27,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ArrayRange<RenderTreeFrame> oldTree,
ArrayRange<RenderTreeFrame> newTree)
{
ComponentsProfiling.Instance.Start();
var editsBuffer = batchBuilder.EditsBuffer;
var editsBufferStartLength = editsBuffer.Count;
@ -37,7 +35,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count);
var result = new RenderTreeDiff(componentId, editsSegment);
ComponentsProfiling.Instance.End();
return result;
}
@ -49,7 +46,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl)
{
ProfilingStart();
// This is deliberately a very large method. Parts of it could be factored out
// into other private methods, but doing so comes at a consequential perf cost,
// because it involves so much parameter passing. You can think of the code here
@ -82,7 +78,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
if (hasMoreOld)
{
ref var oldFrame = ref oldTree[oldStartIndex];
oldSeq = oldFrame.Sequence;
oldSeq = oldFrame.SequenceField;
oldKey = KeyValue(ref oldFrame);
}
else
@ -94,7 +90,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
if (hasMoreNew)
{
ref var newFrame = ref newTree[newStartIndex];
newSeq = newFrame.Sequence;
newSeq = newFrame.SequenceField;
newKey = KeyValue(ref newFrame);
}
else
@ -199,7 +195,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var newLoopsBackLater = false;
for (var testIndex = newStartIndex + 1; testIndex < newEndIndexExcl; testIndex++)
{
if (newTree[testIndex].Sequence < newSeq)
if (newTree[testIndex].SequenceField < newSeq)
{
newLoopsBackLater = true;
break;
@ -222,7 +218,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var oldLoopsBackLater = false;
for (var testIndex = oldStartIndex + 1; testIndex < oldEndIndexExcl; testIndex++)
{
if (oldTree[testIndex].Sequence < oldSeq)
if (oldTree[testIndex].SequenceField < oldSeq)
{
oldLoopsBackLater = true;
break;
@ -300,12 +296,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
diffContext.KeyedItemInfoDictionaryPool.Return(keyedItemInfos);
}
}
ProfilingEnd();
}
private static Dictionary<object, KeyedItemInfo> BuildKeyToInfoLookup(DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl)
{
ProfilingStart();
var result = diffContext.KeyedItemInfoDictionaryPool.Get();
var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree;
@ -318,7 +312,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
if (result.ContainsKey(key))
{
ThrowExceptionForDuplicateKey(key);
ThrowExceptionForDuplicateKey(key, frame);
}
result[key] = new KeyedItemInfo(oldStartIndex, -1);
@ -341,7 +335,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
if (existingEntry.NewIndex >= 0)
{
ThrowExceptionForDuplicateKey(key);
ThrowExceptionForDuplicateKey(key, frame);
}
result[key] = new KeyedItemInfo(existingEntry.OldIndex, newStartIndex);
@ -351,23 +345,32 @@ namespace Microsoft.AspNetCore.Components.RenderTree
newStartIndex = NextSiblingIndex(frame, newStartIndex);
}
ProfilingEnd();
return result;
}
private static void ThrowExceptionForDuplicateKey(object key)
private static void ThrowExceptionForDuplicateKey(object key, in RenderTreeFrame frame)
{
throw new InvalidOperationException($"More than one sibling has the same key value, '{key}'. Key values must be unique.");
switch (frame.FrameTypeField)
{
case RenderTreeFrameType.Component:
throw new InvalidOperationException($"More than one sibling of component '{frame.ComponentTypeField}' has the same key value, '{key}'. Key values must be unique.");
case RenderTreeFrameType.Element:
throw new InvalidOperationException($"More than one sibling of element '{frame.ElementNameField}' has the same key value, '{key}'. Key values must be unique.");
default:
throw new InvalidOperationException($"More than one sibling has the same key value, '{key}'. Key values must be unique.");
}
}
private static object KeyValue(ref RenderTreeFrame frame)
{
switch (frame.FrameType)
switch (frame.FrameTypeField)
{
case RenderTreeFrameType.Element:
return frame.ElementKey;
return frame.ElementKeyField;
case RenderTreeFrameType.Component:
return frame.ComponentKey;
return frame.ComponentKeyField;
default:
return null;
}
@ -384,7 +387,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl)
{
ProfilingStart();
// The overhead of the dictionary used by AppendAttributeDiffEntriesForRangeSlow is
// significant, so we want to try and do a merge-join if possible, but fall back to
// a hash-join if not. We'll do a merge join until we hit a case we can't handle and
@ -403,10 +405,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree
while (hasMoreOld || hasMoreNew)
{
var oldSeq = hasMoreOld ? oldTree[oldStartIndex].Sequence : int.MaxValue;
var newSeq = hasMoreNew ? newTree[newStartIndex].Sequence : int.MaxValue;
var oldAttributeName = oldTree[oldStartIndex].AttributeName;
var newAttributeName = newTree[newStartIndex].AttributeName;
var oldSeq = hasMoreOld ? oldTree[oldStartIndex].SequenceField : int.MaxValue;
var newSeq = hasMoreNew ? newTree[newStartIndex].SequenceField : int.MaxValue;
var oldAttributeName = oldTree[oldStartIndex].AttributeNameField;
var newAttributeName = newTree[newStartIndex].AttributeNameField;
if (oldSeq == newSeq &&
string.Equals(oldAttributeName, newAttributeName, StringComparison.Ordinal))
@ -433,7 +435,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ref diffContext,
oldStartIndex, oldEndIndexExcl,
newStartIndex, newEndIndexExcl);
ProfilingEnd();
return;
}
@ -459,12 +460,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ref diffContext,
oldStartIndex, oldEndIndexExcl,
newStartIndex, newEndIndexExcl);
ProfilingEnd();
return;
}
}
ProfilingEnd();
}
private static void AppendAttributeDiffEntriesForRangeSlow(
@ -472,7 +470,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldStartIndex, int oldEndIndexExcl,
int newStartIndex, int newEndIndexExcl)
{
ProfilingStart();
var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree;
@ -484,12 +481,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// 3. iterate through the remaining attributes in the set and add them
for (var i = newStartIndex; i < newEndIndexExcl; i++)
{
diffContext.AttributeDiffSet[newTree[i].AttributeName] = i;
diffContext.AttributeDiffSet[newTree[i].AttributeNameField] = i;
}
for (var i = oldStartIndex; i < oldEndIndexExcl; i++)
{
var oldName = oldTree[i].AttributeName;
var oldName = oldTree[i].AttributeNameField;
if (diffContext.AttributeDiffSet.TryGetValue(oldName, out var matchIndex))
{
// Has a match in the new tree, look for a diff
@ -511,7 +508,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// We should have processed any additions at this point. Reset for the next batch.
diffContext.AttributeDiffSet.Clear();
ProfilingEnd();
}
private static void UpdateRetainedChildComponent(
@ -519,15 +515,15 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldComponentIndex,
int newComponentIndex)
{
ProfilingStart();
var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree;
ref var oldComponentFrame = ref oldTree[oldComponentIndex];
ref var newComponentFrame = ref newTree[newComponentIndex];
var componentState = oldComponentFrame.ComponentState;
var componentState = oldComponentFrame.ComponentStateField;
// Preserve the actual componentInstance
newComponentFrame = newComponentFrame.WithComponent(componentState);
newComponentFrame.ComponentStateField = componentState;
newComponentFrame.ComponentIdField = componentState.ComponentId;
// As an important rendering optimization, we want to skip parameter update
// notifications if we know for sure they haven't changed/mutated. The
@ -546,20 +542,18 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
componentState.SetDirectParameters(newParameters);
}
ProfilingEnd();
}
private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex)
{
switch (frame.FrameType)
switch (frame.FrameTypeField)
{
case RenderTreeFrameType.Component:
return frameIndex + frame.ComponentSubtreeLength;
return frameIndex + frame.ComponentSubtreeLengthField;
case RenderTreeFrameType.Element:
return frameIndex + frame.ElementSubtreeLength;
return frameIndex + frame.ElementSubtreeLengthField;
case RenderTreeFrameType.Region:
return frameIndex + frame.RegionSubtreeLength;
return frameIndex + frame.RegionSubtreeLengthField;
default:
return frameIndex + 1;
}
@ -570,7 +564,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldFrameIndex,
int newFrameIndex)
{
ProfilingStart();
var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree;
ref var oldFrame = ref oldTree[oldFrameIndex];
@ -578,12 +571,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// This can't happen for sequence-matched frames from .razor components, but it can happen if you write your
// builder logic manually or if two dissimilar frames matched by key. Treat as completely unrelated.
var newFrameType = newFrame.FrameType;
if (oldFrame.FrameType != newFrameType)
var newFrameType = newFrame.FrameTypeField;
if (oldFrame.FrameTypeField != newFrameType)
{
InsertNewFrame(ref diffContext, newFrameIndex);
RemoveOldFrame(ref diffContext, oldFrameIndex);
ProfilingEnd();
return;
}
@ -595,8 +587,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
case RenderTreeFrameType.Text:
{
var oldText = oldFrame.TextContent;
var newText = newFrame.TextContent;
var oldText = oldFrame.TextContentField;
var newText = newFrame.TextContentField;
if (!string.Equals(oldText, newText, StringComparison.Ordinal))
{
var referenceFrameIndex = diffContext.ReferenceFrames.Append(newFrame);
@ -608,8 +600,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
case RenderTreeFrameType.Markup:
{
var oldMarkup = oldFrame.MarkupContent;
var newMarkup = newFrame.MarkupContent;
var oldMarkup = oldFrame.MarkupContentField;
var newMarkup = newFrame.MarkupContentField;
if (!string.Equals(oldMarkup, newMarkup, StringComparison.Ordinal))
{
var referenceFrameIndex = diffContext.ReferenceFrames.Append(newFrame);
@ -621,8 +613,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
case RenderTreeFrameType.Element:
{
var oldElementName = oldFrame.ElementName;
var newElementName = newFrame.ElementName;
var oldElementName = oldFrame.ElementNameField;
var newElementName = newFrame.ElementNameField;
if (string.Equals(oldElementName, newElementName, StringComparison.Ordinal))
{
var oldFrameAttributesEndIndexExcl = GetAttributesEndIndexExclusive(oldTree, oldFrameIndex);
@ -635,8 +627,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
newFrameIndex + 1, newFrameAttributesEndIndexExcl);
// Diff the children
var oldFrameChildrenEndIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLength;
var newFrameChildrenEndIndexExcl = newFrameIndex + newFrame.ElementSubtreeLength;
var oldFrameChildrenEndIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLengthField;
var newFrameChildrenEndIndexExcl = newFrameIndex + newFrame.ElementSubtreeLengthField;
var hasChildrenToProcess =
oldFrameChildrenEndIndexExcl > oldFrameAttributesEndIndexExcl ||
newFrameChildrenEndIndexExcl > newFrameAttributesEndIndexExcl;
@ -670,14 +662,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
AppendDiffEntriesForRange(
ref diffContext,
oldFrameIndex + 1, oldFrameIndex + oldFrame.RegionSubtreeLength,
newFrameIndex + 1, newFrameIndex + newFrame.RegionSubtreeLength);
oldFrameIndex + 1, oldFrameIndex + oldFrame.RegionSubtreeLengthField,
newFrameIndex + 1, newFrameIndex + newFrame.RegionSubtreeLengthField);
break;
}
case RenderTreeFrameType.Component:
{
if (oldFrame.ComponentType == newFrame.ComponentType)
if (oldFrame.ComponentTypeField == newFrame.ComponentTypeField)
{
UpdateRetainedChildComponent(
ref diffContext,
@ -707,10 +699,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// We don't handle attributes here, they have their own diff logic.
// See AppendDiffEntriesForAttributeFrame
default:
throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameType}");
throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameTypeField}");
}
ProfilingEnd();
}
// This should only be called for attributes that have the same name. This is an
@ -720,14 +710,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree
int oldFrameIndex,
int newFrameIndex)
{
ProfilingStart();
var oldTree = diffContext.OldTree;
var newTree = diffContext.NewTree;
ref var oldFrame = ref oldTree[oldFrameIndex];
ref var newFrame = ref newTree[newFrameIndex];
// Using Equals to account for string comparisons, nulls, etc.
var valueChanged = !Equals(oldFrame.AttributeValue, newFrame.AttributeValue);
var valueChanged = !Equals(oldFrame.AttributeValueField, newFrame.AttributeValueField);
if (valueChanged)
{
InitializeNewAttributeFrame(ref diffContext, ref newFrame);
@ -736,29 +725,26 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// If we're replacing an old event handler ID with a new one, register the old one for disposal,
// plus keep track of the old->new chain until the old one is fully disposed
if (oldFrame.AttributeEventHandlerId > 0)
if (oldFrame.AttributeEventHandlerIdField > 0)
{
diffContext.Renderer.TrackReplacedEventHandlerId(oldFrame.AttributeEventHandlerId, newFrame.AttributeEventHandlerId);
diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerId);
diffContext.Renderer.TrackReplacedEventHandlerId(oldFrame.AttributeEventHandlerIdField, newFrame.AttributeEventHandlerIdField);
diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerIdField);
}
}
else if (oldFrame.AttributeEventHandlerId > 0)
else if (oldFrame.AttributeEventHandlerIdField > 0)
{
// Retain the event handler ID by copying the old frame over the new frame.
// this will prevent us from needing to dispose the old event handler
// since it was unchanged.
newFrame = oldFrame;
}
ProfilingEnd();
}
private static void InsertNewFrame(ref DiffContext diffContext, int newFrameIndex)
{
ProfilingStart();
var newTree = diffContext.NewTree;
ref var newFrame = ref newTree[newFrameIndex];
switch (newFrame.FrameType)
switch (newFrame.FrameTypeField)
{
case RenderTreeFrameType.Attribute:
{
@ -771,7 +757,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
case RenderTreeFrameType.Element:
{
InitializeNewSubtree(ref diffContext, newFrameIndex);
var referenceFrameIndex = diffContext.ReferenceFrames.Append(newTree, newFrameIndex, newFrame.ElementSubtreeLength);
var referenceFrameIndex = diffContext.ReferenceFrames.Append(newTree, newFrameIndex, newFrame.ElementSubtreeLengthField);
diffContext.Edits.Append(RenderTreeEdit.PrependFrame(diffContext.SiblingIndex, referenceFrameIndex));
diffContext.SiblingIndex++;
break;
@ -779,7 +765,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
case RenderTreeFrameType.Region:
{
var regionChildFrameIndex = newFrameIndex + 1;
var regionChildFrameEndIndexExcl = newFrameIndex + newFrame.RegionSubtreeLength;
var regionChildFrameEndIndexExcl = newFrameIndex + newFrame.RegionSubtreeLengthField;
while (regionChildFrameIndex < regionChildFrameEndIndexExcl)
{
InsertNewFrame(ref diffContext, regionChildFrameIndex);
@ -806,31 +792,29 @@ namespace Microsoft.AspNetCore.Components.RenderTree
break;
}
default:
throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameType}");
throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameTypeField}");
}
ProfilingEnd();
}
private static void RemoveOldFrame(ref DiffContext diffContext, int oldFrameIndex)
{
ProfilingStart();
var oldTree = diffContext.OldTree;
ref var oldFrame = ref oldTree[oldFrameIndex];
switch (oldFrame.FrameType)
switch (oldFrame.FrameTypeField)
{
case RenderTreeFrameType.Attribute:
{
diffContext.Edits.Append(RenderTreeEdit.RemoveAttribute(diffContext.SiblingIndex, oldFrame.AttributeName));
if (oldFrame.AttributeEventHandlerId > 0)
diffContext.Edits.Append(RenderTreeEdit.RemoveAttribute(diffContext.SiblingIndex, oldFrame.AttributeNameField));
if (oldFrame.AttributeEventHandlerIdField > 0)
{
diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerId);
diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerIdField);
}
break;
}
case RenderTreeFrameType.Component:
case RenderTreeFrameType.Element:
{
var endIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLength;
var endIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLengthField;
DisposeFramesInRange(diffContext.BatchBuilder, oldTree, oldFrameIndex, endIndexExcl);
diffContext.Edits.Append(RenderTreeEdit.RemoveFrame(diffContext.SiblingIndex));
break;
@ -838,7 +822,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
case RenderTreeFrameType.Region:
{
var regionChildFrameIndex = oldFrameIndex + 1;
var regionChildFrameEndIndexExcl = oldFrameIndex + oldFrame.RegionSubtreeLength;
var regionChildFrameEndIndexExcl = oldFrameIndex + oldFrame.RegionSubtreeLengthField;
while (regionChildFrameIndex < regionChildFrameEndIndexExcl)
{
RemoveOldFrame(ref diffContext, regionChildFrameIndex);
@ -853,18 +837,17 @@ namespace Microsoft.AspNetCore.Components.RenderTree
break;
}
default:
throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameType}");
throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameTypeField}");
}
ProfilingEnd();
}
private static int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex)
{
var descendantsEndIndexExcl = rootIndex + tree[rootIndex].ElementSubtreeLength;
var descendantsEndIndexExcl = rootIndex + tree[rootIndex].ElementSubtreeLengthField;
var index = rootIndex + 1;
for (; index < descendantsEndIndexExcl; index++)
{
if (tree[index].FrameType != RenderTreeFrameType.Attribute)
if (tree[index].FrameTypeField != RenderTreeFrameType.Attribute)
{
break;
}
@ -889,13 +872,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private static void InitializeNewSubtree(ref DiffContext diffContext, int frameIndex)
{
ProfilingStart();
var frames = diffContext.NewTree;
var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength;
var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLengthField;
for (var i = frameIndex; i < endIndexExcl; i++)
{
ref var frame = ref frames[i];
switch (frame.FrameType)
switch (frame.FrameTypeField)
{
case RenderTreeFrameType.Component:
InitializeNewComponentFrame(ref diffContext, i);
@ -911,29 +893,26 @@ namespace Microsoft.AspNetCore.Components.RenderTree
break;
}
}
ProfilingEnd();
}
private static void InitializeNewComponentFrame(ref DiffContext diffContext, int frameIndex)
{
ProfilingStart();
var frames = diffContext.NewTree;
ref var frame = ref frames[frameIndex];
if (frame.ComponentState != null)
if (frame.ComponentStateField != null)
{
throw new InvalidOperationException($"Child component already exists during {nameof(InitializeNewComponentFrame)}");
}
var parentComponentId = diffContext.ComponentId;
diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame, parentComponentId);
var childComponentState = frame.ComponentState;
var childComponentState = frame.ComponentStateField;
// Set initial parameters
var initialParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder);
var initialParameters = new ParameterView(initialParametersLifetime, frames, frameIndex);
childComponentState.SetDirectParameters(initialParameters);
ProfilingEnd();
}
private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
@ -942,9 +921,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree
//
// We're following a simple heuristic here that's reflected in the ts runtime
// based on the common usage of attributes for DOM events.
if ((newFrame.AttributeValue is MulticastDelegate || newFrame.AttributeValue is EventCallback) &&
newFrame.AttributeName.Length >= 3 &&
newFrame.AttributeName.StartsWith("on"))
if ((newFrame.AttributeValueField is MulticastDelegate || newFrame.AttributeValueField is EventCallback) &&
newFrame.AttributeNameField.Length >= 3 &&
newFrame.AttributeNameField.StartsWith("on", StringComparison.Ordinal))
{
diffContext.Renderer.AssignEventHandlerId(ref newFrame);
}
@ -953,14 +932,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private static void InitializeNewElementReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
{
var newElementReference = ElementReference.CreateWithUniqueId(diffContext.Renderer.ElementReferenceContext);
newFrame = newFrame.WithElementReferenceCaptureId(newElementReference.Id);
newFrame.ElementReferenceCaptureAction(newElementReference);
newFrame.ElementReferenceCaptureIdField = newElementReference.Id;
newFrame.ElementReferenceCaptureActionField(newElementReference);
}
private static void InitializeNewComponentReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
{
ref var parentFrame = ref diffContext.NewTree[newFrame.ComponentReferenceCaptureParentFrameIndex];
if (parentFrame.FrameType != RenderTreeFrameType.Component)
ref var parentFrame = ref diffContext.NewTree[newFrame.ComponentReferenceCaptureParentFrameIndexField];
if (parentFrame.FrameTypeField != RenderTreeFrameType.Component)
{
// Should never happen, but will help with diagnosis if it does
throw new InvalidOperationException($"{nameof(RenderTreeFrameType.ComponentReferenceCapture)} frame references invalid parent index.");
@ -973,25 +952,23 @@ namespace Microsoft.AspNetCore.Components.RenderTree
throw new InvalidOperationException($"Trying to initialize {nameof(RenderTreeFrameType.ComponentReferenceCapture)} frame before parent component was assigned.");
}
newFrame.ComponentReferenceCaptureAction(componentInstance);
newFrame.ComponentReferenceCaptureActionField(componentInstance);
}
private static void DisposeFramesInRange(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int startIndex, int endIndexExcl)
{
ProfilingStart();
for (var i = startIndex; i < endIndexExcl; i++)
{
ref var frame = ref frames[i];
if (frame.FrameType == RenderTreeFrameType.Component && frame.ComponentState != null)
if (frame.FrameTypeField == RenderTreeFrameType.Component && frame.ComponentStateField != null)
{
batchBuilder.ComponentDisposalQueue.Enqueue(frame.ComponentId);
batchBuilder.ComponentDisposalQueue.Enqueue(frame.ComponentIdField);
}
else if (frame.FrameType == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerId > 0)
else if (frame.FrameTypeField == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerIdField > 0)
{
batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerId);
batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerIdField);
}
}
ProfilingEnd();
}
/// <summary>
@ -1033,18 +1010,5 @@ namespace Microsoft.AspNetCore.Components.RenderTree
SiblingIndex = 0;
}
}
// Having too many calls to ComponentsProfiling.Instance.Start/End has a measurable perf impact
// even when capturing is disabled. So, to enable detailed profiling for this class, define the
// Profile_RenderTreeDiffBuilder compiler symbol, otherwise the calls are compiled out entirely.
// Enabling detailed profiling adds about 5% to rendering benchmark times.
[Conditional("Profile_RenderTreeDiffBuilder")]
private static void ProfilingStart([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.Start(name);
[Conditional("Profile_RenderTreeDiffBuilder")]
private static void ProfilingEnd([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.End(name);
}
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
//
// Represents an entry in a tree of user interface (UI) items.
[StructLayout(LayoutKind.Explicit, Pack = 4)]
public readonly struct RenderTreeFrame
public struct RenderTreeFrame
{
// Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms,
// which means that all reference-type fields need to take up 8 bytes (except for the last
@ -48,72 +48,86 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// Common
// --------------------------------------------------------------------------------
[FieldOffset(0)] internal int SequenceField;
[FieldOffset(4)] internal RenderTreeFrameType FrameTypeField;
/// <summary>
/// Gets the sequence number of the frame. Sequence numbers indicate the relative source
/// positions of the instructions that inserted the frames. Sequence numbers are only
/// comparable within the same sequence (typically, the same source method).
/// </summary>
[FieldOffset(0)] public readonly int Sequence;
public int Sequence => SequenceField;
/// <summary>
/// Describes the type of this frame.
/// </summary>
[FieldOffset(4)] public readonly RenderTreeFrameType FrameType;
public RenderTreeFrameType FrameType => FrameTypeField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Element
// --------------------------------------------------------------------------------
[FieldOffset(8)] internal int ElementSubtreeLengthField;
[FieldOffset(16)] internal string ElementNameField;
[FieldOffset(24)] internal object ElementKeyField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>
/// gets the number of frames in the subtree for which this frame is the root.
/// The value is zero if the frame has not yet been closed.
/// </summary>
[FieldOffset(8)] public readonly int ElementSubtreeLength;
public int ElementSubtreeLength => ElementSubtreeLengthField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
/// gets a name representing the type of the element. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly string ElementName;
public string ElementName => ElementNameField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
/// gets the element's diffing key, or null if none was specified.
/// </summary>
[FieldOffset(24)] public readonly object ElementKey;
public object ElementKey => ElementKeyField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Text
// --------------------------------------------------------------------------------
[FieldOffset(16)] internal string TextContentField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
/// gets the content of the text frame. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly string TextContent;
public string TextContent => TextContentField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Attribute
// --------------------------------------------------------------------------------
[FieldOffset(8)] internal ulong AttributeEventHandlerIdField;
[FieldOffset(16)] internal string AttributeNameField;
[FieldOffset(24)] internal object AttributeValueField;
[FieldOffset(32)] internal string AttributeEventUpdatesAttributeNameField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>
/// gets the ID of the corresponding event handler, if any.
/// </summary>
[FieldOffset(8)] public readonly ulong AttributeEventHandlerId;
public ulong AttributeEventHandlerId => AttributeEventHandlerIdField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
/// gets the attribute name. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly string AttributeName;
public string AttributeName => AttributeNameField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
/// gets the attribute value. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(24)] public readonly object AttributeValue;
public object AttributeValue => AttributeValueField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
@ -121,80 +135,94 @@ namespace Microsoft.AspNetCore.Components.RenderTree
/// can be updated to represent the UI state prior to executing the event handler. This is
/// primarily used in two-way bindings.
/// </summary>
[FieldOffset(32)] public readonly string AttributeEventUpdatesAttributeName;
public string AttributeEventUpdatesAttributeName => AttributeEventUpdatesAttributeNameField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Component
// --------------------------------------------------------------------------------
[FieldOffset(8)] internal int ComponentSubtreeLengthField;
[FieldOffset(12)] internal int ComponentIdField;
[FieldOffset(16)] internal Type ComponentTypeField;
[FieldOffset(24)] internal ComponentState ComponentStateField;
[FieldOffset(32)] internal object ComponentKeyField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>
/// gets the number of frames in the subtree for which this frame is the root.
/// The value is zero if the frame has not yet been closed.
/// </summary>
[FieldOffset(8)] public readonly int ComponentSubtreeLength;
public int ComponentSubtreeLength => ComponentSubtreeLengthField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the child component instance identifier.
/// </summary>
[FieldOffset(12)] public readonly int ComponentId;
public int ComponentId => ComponentIdField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the type of the child component.
/// </summary>
[FieldOffset(16)] public readonly Type ComponentType;
public Type ComponentType => ComponentTypeField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the child component state object. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(24)] internal readonly ComponentState ComponentState;
internal ComponentState ComponentState => ComponentStateField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the component's diffing key, or null if none was specified.
/// </summary>
[FieldOffset(32)] public readonly object ComponentKey;
public object ComponentKey => ComponentKeyField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the child component instance. Otherwise, the value is undefined.
/// </summary>
public IComponent Component => ComponentState?.Component;
public IComponent Component => ComponentStateField?.Component;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Region
// --------------------------------------------------------------------------------
[FieldOffset(8)] internal int RegionSubtreeLengthField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Region"/>
/// gets the number of frames in the subtree for which this frame is the root.
/// The value is zero if the frame has not yet been closed.
/// </summary>
[FieldOffset(8)] public readonly int RegionSubtreeLength;
public int RegionSubtreeLength => RegionSubtreeLengthField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.ElementReferenceCapture
// --------------------------------------------------------------------------------
[FieldOffset(16)] internal string ElementReferenceCaptureIdField;
[FieldOffset(24)] internal Action<ElementReference> ElementReferenceCaptureActionField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
/// gets the ID of the reference capture. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly string ElementReferenceCaptureId;
public string ElementReferenceCaptureId => ElementReferenceCaptureIdField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(24)] public readonly Action<ElementReference> ElementReferenceCaptureAction;
public Action<ElementReference> ElementReferenceCaptureAction => ElementReferenceCaptureActionField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.ComponentReferenceCapture
// --------------------------------------------------------------------------------
[FieldOffset(8)] internal int ComponentReferenceCaptureParentFrameIndexField;
[FieldOffset(16)] internal Action<object> ComponentReferenceCaptureActionField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
/// gets the index of the parent frame representing the component being captured. Otherwise, the value is undefined.
@ -205,49 +233,51 @@ namespace Microsoft.AspNetCore.Components.RenderTree
/// initialization logic in RenderTreeDiffBuilder to walk the frames hierarchically, then it would know
/// the parent index at the point where it wants to initialize the ComponentReferenceCapture frame.
/// </summary>
[FieldOffset(8)] public readonly int ComponentReferenceCaptureParentFrameIndex;
public int ComponentReferenceCaptureParentFrameIndex => ComponentReferenceCaptureParentFrameIndexField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly Action<object> ComponentReferenceCaptureAction;
public Action<object> ComponentReferenceCaptureAction => ComponentReferenceCaptureActionField;
// --------------------------------------------------------------------------------
// RenderTreeFrameType.Markup
// --------------------------------------------------------------------------------
[FieldOffset(16)] internal string MarkupContentField;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Markup"/>,
/// gets the content of the markup frame. Otherwise, the value is undefined.
/// </summary>
[FieldOffset(16)] public readonly string MarkupContent;
public string MarkupContent => MarkupContentField;
// Element constructor
private RenderTreeFrame(int sequence, int elementSubtreeLength, string elementName, object elementKey)
: this()
{
Sequence = sequence;
FrameType = RenderTreeFrameType.Element;
ElementSubtreeLength = elementSubtreeLength;
ElementName = elementName;
ElementKey = elementKey;
SequenceField = sequence;
FrameTypeField = RenderTreeFrameType.Element;
ElementSubtreeLengthField = elementSubtreeLength;
ElementNameField = elementName;
ElementKeyField = elementKey;
}
// Component constructor
private RenderTreeFrame(int sequence, int componentSubtreeLength, Type componentType, ComponentState componentState, object componentKey)
: this()
{
Sequence = sequence;
FrameType = RenderTreeFrameType.Component;
ComponentSubtreeLength = componentSubtreeLength;
ComponentType = componentType;
ComponentKey = componentKey;
SequenceField = sequence;
FrameTypeField = RenderTreeFrameType.Component;
ComponentSubtreeLengthField = componentSubtreeLength;
ComponentTypeField = componentType;
ComponentKeyField = componentKey;
if (componentState != null)
{
ComponentState = componentState;
ComponentId = componentState.ComponentId;
ComponentStateField = componentState;
ComponentIdField = componentState.ComponentId;
}
}
@ -255,25 +285,25 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private RenderTreeFrame(int sequence, int regionSubtreeLength)
: this()
{
Sequence = sequence;
FrameType = RenderTreeFrameType.Region;
RegionSubtreeLength = regionSubtreeLength;
SequenceField = sequence;
FrameTypeField = RenderTreeFrameType.Region;
RegionSubtreeLengthField = regionSubtreeLength;
}
// Text/markup constructor
private RenderTreeFrame(int sequence, bool isMarkup, string textOrMarkup)
: this()
{
Sequence = sequence;
SequenceField = sequence;
if (isMarkup)
{
FrameType = RenderTreeFrameType.Markup;
MarkupContent = textOrMarkup;
FrameTypeField = RenderTreeFrameType.Markup;
MarkupContentField = textOrMarkup;
}
else
{
FrameType = RenderTreeFrameType.Text;
TextContent = textOrMarkup;
FrameTypeField = RenderTreeFrameType.Text;
TextContentField = textOrMarkup;
}
}
@ -281,32 +311,32 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private RenderTreeFrame(int sequence, string attributeName, object attributeValue, ulong attributeEventHandlerId, string attributeEventUpdatesAttributeName)
: this()
{
FrameType = RenderTreeFrameType.Attribute;
Sequence = sequence;
AttributeName = attributeName;
AttributeValue = attributeValue;
AttributeEventHandlerId = attributeEventHandlerId;
AttributeEventUpdatesAttributeName = attributeEventUpdatesAttributeName;
FrameTypeField = RenderTreeFrameType.Attribute;
SequenceField = sequence;
AttributeNameField = attributeName;
AttributeValueField = attributeValue;
AttributeEventHandlerIdField = attributeEventHandlerId;
AttributeEventUpdatesAttributeNameField = attributeEventUpdatesAttributeName;
}
// Element reference capture constructor
private RenderTreeFrame(int sequence, Action<ElementReference> elementReferenceCaptureAction, string elementReferenceCaptureId)
: this()
{
FrameType = RenderTreeFrameType.ElementReferenceCapture;
Sequence = sequence;
ElementReferenceCaptureAction = elementReferenceCaptureAction;
ElementReferenceCaptureId = elementReferenceCaptureId;
FrameTypeField = RenderTreeFrameType.ElementReferenceCapture;
SequenceField = sequence;
ElementReferenceCaptureActionField = elementReferenceCaptureAction;
ElementReferenceCaptureIdField = elementReferenceCaptureId;
}
// Component reference capture constructor
private RenderTreeFrame(int sequence, Action<object> componentReferenceCaptureAction, int parentFrameIndex)
: this()
{
FrameType = RenderTreeFrameType.ComponentReferenceCapture;
Sequence = sequence;
ComponentReferenceCaptureAction = componentReferenceCaptureAction;
ComponentReferenceCaptureParentFrameIndex = parentFrameIndex;
FrameTypeField = RenderTreeFrameType.ComponentReferenceCapture;
SequenceField = sequence;
ComponentReferenceCaptureActionField = componentReferenceCaptureAction;
ComponentReferenceCaptureParentFrameIndexField = parentFrameIndex;
}
internal static RenderTreeFrame Element(int sequence, string elementName)
@ -337,61 +367,61 @@ namespace Microsoft.AspNetCore.Components.RenderTree
=> new RenderTreeFrame(sequence, componentReferenceCaptureAction: componentReferenceCaptureAction, parentFrameIndex: parentFrameIndex);
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
=> new RenderTreeFrame(Sequence, elementSubtreeLength: elementSubtreeLength, ElementName, ElementKey);
=> new RenderTreeFrame(SequenceField, elementSubtreeLength: elementSubtreeLength, ElementNameField, ElementKeyField);
internal RenderTreeFrame WithComponentSubtreeLength(int componentSubtreeLength)
=> new RenderTreeFrame(Sequence, componentSubtreeLength: componentSubtreeLength, ComponentType, ComponentState, ComponentKey);
=> new RenderTreeFrame(SequenceField, componentSubtreeLength: componentSubtreeLength, ComponentTypeField, ComponentStateField, ComponentKeyField);
internal RenderTreeFrame WithAttributeSequence(int sequence)
=> new RenderTreeFrame(sequence, attributeName: AttributeName, AttributeValue, AttributeEventHandlerId, AttributeEventUpdatesAttributeName);
=> new RenderTreeFrame(sequence, attributeName: AttributeNameField, AttributeValueField, AttributeEventHandlerIdField, AttributeEventUpdatesAttributeNameField);
internal RenderTreeFrame WithComponent(ComponentState componentState)
=> new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, componentState, ComponentKey);
=> new RenderTreeFrame(SequenceField, componentSubtreeLength: ComponentSubtreeLengthField, ComponentTypeField, componentState, ComponentKeyField);
internal RenderTreeFrame WithAttributeEventHandlerId(ulong eventHandlerId)
=> new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, eventHandlerId, AttributeEventUpdatesAttributeName);
=> new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, AttributeValueField, eventHandlerId, AttributeEventUpdatesAttributeNameField);
internal RenderTreeFrame WithAttributeValue(object attributeValue)
=> new RenderTreeFrame(Sequence, attributeName: AttributeName, attributeValue, AttributeEventHandlerId, AttributeEventUpdatesAttributeName);
=> new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, attributeValue, AttributeEventHandlerIdField, AttributeEventUpdatesAttributeNameField);
internal RenderTreeFrame WithAttributeEventUpdatesAttributeName(string attributeUpdatesAttributeName)
=> new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, AttributeEventHandlerId, attributeUpdatesAttributeName);
=> new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, AttributeValueField, AttributeEventHandlerIdField, attributeUpdatesAttributeName);
internal RenderTreeFrame WithRegionSubtreeLength(int regionSubtreeLength)
=> new RenderTreeFrame(Sequence, regionSubtreeLength: regionSubtreeLength);
=> new RenderTreeFrame(SequenceField, regionSubtreeLength: regionSubtreeLength);
internal RenderTreeFrame WithElementReferenceCaptureId(string elementReferenceCaptureId)
=> new RenderTreeFrame(Sequence, elementReferenceCaptureAction: ElementReferenceCaptureAction, elementReferenceCaptureId);
=> new RenderTreeFrame(SequenceField, elementReferenceCaptureAction: ElementReferenceCaptureActionField, elementReferenceCaptureId);
internal RenderTreeFrame WithElementKey(object elementKey)
=> new RenderTreeFrame(Sequence, elementSubtreeLength: ElementSubtreeLength, ElementName, elementKey);
=> new RenderTreeFrame(SequenceField, elementSubtreeLength: ElementSubtreeLengthField, ElementNameField, elementKey);
internal RenderTreeFrame WithComponentKey(object componentKey)
=> new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, ComponentState, componentKey);
=> new RenderTreeFrame(SequenceField, componentSubtreeLength: ComponentSubtreeLengthField, ComponentTypeField, ComponentStateField, componentKey);
/// <inheritdoc />
// Just to be nice for debugging and unit tests.
public override string ToString()
{
switch (FrameType)
switch (FrameTypeField)
{
case RenderTreeFrameType.Attribute:
return $"Attribute: (seq={Sequence}, id={AttributeEventHandlerId}) '{AttributeName}'='{AttributeValue}'";
case RenderTreeFrameType.Component:
return $"Component: (seq={Sequence}, key={ComponentKey ?? "(none)"}, len={ComponentSubtreeLength}) {ComponentType}";
return $"Component: (seq={Sequence}, key={ComponentKeyField ?? "(none)"}, len={ComponentSubtreeLength}) {ComponentType}";
case RenderTreeFrameType.Element:
return $"Element: (seq={Sequence}, key={ElementKey ?? "(none)"}, len={ElementSubtreeLength}) {ElementName}";
return $"Element: (seq={Sequence}, key={ElementKeyField ?? "(none)"}, len={ElementSubtreeLength}) {ElementName}";
case RenderTreeFrameType.Region:
return $"Region: (seq={Sequence}, len={RegionSubtreeLength})";
case RenderTreeFrameType.Text:
return $"Text: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}";
return $"Text: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContentField)}";
case RenderTreeFrameType.Markup:
return $"Markup: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}";
return $"Markup: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContentField)}";
case RenderTreeFrameType.ElementReferenceCapture:
return $"ElementReferenceCapture: (seq={Sequence}, len=n/a) {ElementReferenceCaptureAction}";

View File

@ -0,0 +1,137 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// A special subclass of <see cref="ArrayBuilder{T}"/> that contains methods optimized for appending <see cref="RenderTreeFrame"/> entries.
/// </summary>
internal class RenderTreeFrameArrayBuilder : ArrayBuilder<RenderTreeFrame>
{
// You may notice a repeated block at the top of each of these methods. This is intentionally inlined into each
// method because doing so improves intensive rendering scenarios by around 1% (based on the FastGrid benchmark).
public void AppendElement(int sequence, string elementName)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Element,
ElementNameField = elementName,
};
}
public void AppendText(int sequence, string textContent)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Text,
TextContentField = textContent,
};
}
public void AppendMarkup(int sequence, string markupContent)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Markup,
MarkupContentField = markupContent,
};
}
public void AppendAttribute(int sequence, string attributeName, object? attributeValue)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Attribute,
AttributeNameField = attributeName,
AttributeValueField = attributeValue,
};
}
public void AppendComponent(int sequence, Type componentType)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Component,
ComponentTypeField = componentType,
};
}
public void AppendElementReferenceCapture(int sequence, Action<ElementReference> elementReferenceCaptureAction)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.ElementReferenceCapture,
ElementReferenceCaptureActionField = elementReferenceCaptureAction,
};
}
public void AppendComponentReferenceCapture(int sequence, Action<object?> componentReferenceCaptureAction, int parentFrameIndexValue)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.ComponentReferenceCapture,
ComponentReferenceCaptureActionField = componentReferenceCaptureAction,
ComponentReferenceCaptureParentFrameIndexField = parentFrameIndexValue,
};
}
public void AppendRegion(int sequence)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Region,
};
}
}
}

View File

@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -247,7 +246,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
/// </returns>
public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs)
{
ComponentsProfiling.Instance.Start();
Dispatcher.AssertAccess();
if (!_eventBindings.TryGetValue(eventHandlerId, out var callback))
@ -275,7 +273,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
catch (Exception e)
{
HandleException(e);
ComponentsProfiling.Instance.End();
return Task.CompletedTask;
}
finally
@ -290,25 +287,25 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// Task completed synchronously or is still running. We already processed all of the rendering
// work that was queued so let our error handler deal with it.
var result = GetErrorHandledTask(task);
ComponentsProfiling.Instance.End();
return result;
}
internal void InstantiateChildComponentOnFrame(ref RenderTreeFrame frame, int parentComponentId)
{
if (frame.FrameType != RenderTreeFrameType.Component)
if (frame.FrameTypeField != RenderTreeFrameType.Component)
{
throw new ArgumentException($"The frame's {nameof(RenderTreeFrame.FrameType)} property must equal {RenderTreeFrameType.Component}", nameof(frame));
}
if (frame.ComponentState != null)
if (frame.ComponentStateField != null)
{
throw new ArgumentException($"The frame already has a non-null component instance", nameof(frame));
}
var newComponent = InstantiateComponent(frame.ComponentType);
var newComponent = InstantiateComponent(frame.ComponentTypeField);
var newComponentState = AttachAndInitComponent(newComponent, parentComponentId);
frame = frame.WithComponent(newComponentState);
frame.ComponentStateField = newComponentState;
frame.ComponentIdField = newComponentState.ComponentId;
}
internal void AddToPendingTasks(Task task)
@ -346,7 +343,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
var id = ++_lastEventHandlerId;
if (frame.AttributeValue is EventCallback callback)
if (frame.AttributeValueField is EventCallback callback)
{
// We hit this case when a EventCallback object is produced that needs an explicit receiver.
// Common cases for this are "chained bind" or "chained event handler" when a component
@ -356,7 +353,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// the receiver.
_eventBindings.Add(id, callback);
}
else if (frame.AttributeValue is MulticastDelegate @delegate)
else if (frame.AttributeValueField is MulticastDelegate @delegate)
{
// This is the common case for a delegate, where the receiver of the event
// is the same as delegate.Target. In this case since the receiver is implicit we can
@ -368,7 +365,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// NOTE: we do not to handle EventCallback<T> here. EventCallback<T> is only used when passing
// a callback to a component, and never when used to attaching a DOM event handler.
frame = frame.WithAttributeEventHandlerId(id);
frame.AttributeEventHandlerIdField = id;
}
/// <summary>
@ -441,7 +438,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
private void ProcessRenderQueue()
{
ComponentsProfiling.Instance.Start();
Dispatcher.AssertAccess();
if (_isBatchInProgress)
@ -456,7 +452,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
if (_batchBuilder.ComponentRenderQueue.Count == 0)
{
ComponentsProfiling.Instance.End();
return;
}
@ -468,9 +463,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
}
var batch = _batchBuilder.ToBatch();
ComponentsProfiling.Instance.Start(nameof(UpdateDisplayAsync));
updateDisplayTask = UpdateDisplayAsync(batch);
ComponentsProfiling.Instance.End(nameof(UpdateDisplayAsync));
// Fire off the execution of OnAfterRenderAsync, but don't wait for it
// if there is async work to be done.
@ -480,7 +473,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
// Ensure we catch errors while running the render functions of the components.
HandleException(e);
ComponentsProfiling.Instance.End();
return;
}
finally
@ -498,7 +490,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
{
ProcessRenderQueue();
}
ComponentsProfiling.Instance.End();
}
private Task InvokeRenderCompletedCalls(ArrayRange<RenderTreeDiff> updatedComponents, Task updateDisplayTask)
@ -634,11 +625,43 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var disposeComponentId = _batchBuilder.ComponentDisposalQueue.Dequeue();
var disposeComponentState = GetRequiredComponentState(disposeComponentId);
Log.DisposingComponent(_logger, disposeComponentState);
if (!disposeComponentState.TryDisposeInBatch(_batchBuilder, out var exception))
if (!(disposeComponentState.Component is IAsyncDisposable))
{
exceptions ??= new List<Exception>();
exceptions.Add(exception);
if (!disposeComponentState.TryDisposeInBatch(_batchBuilder, out var exception))
{
exceptions ??= new List<Exception>();
exceptions.Add(exception);
}
}
else
{
var result = disposeComponentState.DisposeInBatchAsync(_batchBuilder);
if (result.IsCompleted)
{
if (!result.IsCompletedSuccessfully)
{
exceptions ??= new List<Exception>();
exceptions.Add(result.Exception);
}
}
else
{
AddToPendingTasks(GetHandledAsynchronousDisposalErrorsTask(result));
async Task GetHandledAsynchronousDisposalErrorsTask(Task result)
{
try
{
await result;
}
catch (Exception e)
{
HandleException(e);
}
}
}
}
_componentStateById.Remove(disposeComponentId);
_batchBuilder.DisposedComponentIds.Append(disposeComponentId);
}

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Rendering
@ -57,7 +56,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
public void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
{
ComponentsProfiling.Instance.Start();
// A component might be in the render queue already before getting disposed by an
// earlier entry in the render queue. In that case, rendering is a no-op.
if (_componentWasDisposed)
@ -69,9 +67,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
(CurrentRenderTree, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, CurrentRenderTree);
CurrentRenderTree.Clear();
ComponentsProfiling.Instance.Start("BuildRenderTree");
renderFragment(CurrentRenderTree);
ComponentsProfiling.Instance.End("BuildRenderTree");
var diff = RenderTreeDiffBuilder.ComputeDiff(
_renderer,
@ -81,7 +77,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
CurrentRenderTree.GetFrames());
batchBuilder.UpdatedComponentDiffs.Append(diff);
batchBuilder.InvalidateParameterViews();
ComponentsProfiling.Instance.End();
}
public bool TryDisposeInBatch(RenderBatchBuilder batchBuilder, [NotNullWhen(false)] out Exception? exception)
@ -101,6 +96,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
exception = ex;
}
CleanupComponentStateResources(batchBuilder);
return exception == null;
}
private void CleanupComponentStateResources(RenderBatchBuilder batchBuilder)
{
// We don't expect these things to throw.
RenderTreeDiffBuilder.DisposeFrames(batchBuilder, CurrentRenderTree.GetFrames());
@ -110,8 +112,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
DisposeBuffers();
return exception == null;
}
// Callers expect this method to always return a faulted task.
@ -222,5 +222,31 @@ namespace Microsoft.AspNetCore.Components.Rendering
((IDisposable)CurrentRenderTree).Dispose();
_latestDirectParametersSnapshot?.Dispose();
}
public Task DisposeInBatchAsync(RenderBatchBuilder batchBuilder)
{
_componentWasDisposed = true;
CleanupComponentStateResources(batchBuilder);
try
{
var result = ((IAsyncDisposable)Component).DisposeAsync();
if (result.IsCompletedSuccessfully)
{
return Task.CompletedTask;
}
else
{
// We know we are dealing with an exception that happened asynchronously, so return a task
// to the caller so that he can unwrap it.
return result.AsTask();
}
}
catch (Exception e)
{
return Task.FromException(e);
}
}
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components.Profiling;
using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Rendering
@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
private readonly static object BoxedFalse = false;
private readonly static string ComponentReferenceCaptureInvalidParentMessage = $"Component reference captures may only be added as children of frames of type {RenderTreeFrameType.Component}";
private readonly ArrayBuilder<RenderTreeFrame> _entries = new ArrayBuilder<RenderTreeFrame>();
private readonly RenderTreeFrameArrayBuilder _entries = new RenderTreeFrameArrayBuilder();
private readonly Stack<int> _openElementIndices = new Stack<int>();
private RenderTreeFrameType? _lastNonAttributeFrameType;
private bool _hasSeenAddMultipleAttributes;
@ -45,7 +44,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="elementName">A value representing the type of the element.</param>
public void OpenElement(int sequence, string elementName)
{
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes)
@ -55,8 +53,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
_openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.Element(sequence, elementName));
ProfilingEnd();
_entries.AppendElement(sequence, elementName);
_lastNonAttributeFrameType = RenderTreeFrameType.Element;
}
/// <summary>
@ -65,7 +63,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
public void CloseElement()
{
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop();
// We might be closing an element with only attributes, run the duplicate cleanup pass
@ -75,9 +72,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1);
}
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
_entries.Buffer[indexOfEntryBeingClosed].ElementSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed;
}
/// <summary>
@ -87,9 +82,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="markupContent">Content for the new markup frame.</param>
public void AddMarkupContent(int sequence, string? markupContent)
{
ProfilingStart();
Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty));
ProfilingEnd();
_entries.AppendMarkup(sequence, markupContent ?? string.Empty);
_lastNonAttributeFrameType = RenderTreeFrameType.Markup;
}
/// <summary>
@ -99,9 +93,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="textContent">Content for the new text frame.</param>
public void AddContent(int sequence, string? textContent)
{
ProfilingStart();
Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
ProfilingEnd();
_entries.AppendText(sequence, textContent ?? string.Empty);
_lastNonAttributeFrameType = RenderTreeFrameType.Text;
}
/// <summary>
@ -111,7 +104,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="fragment">Content to append.</param>
public void AddContent(int sequence, RenderFragment? fragment)
{
ProfilingStart();
if (fragment != null)
{
// We surround the fragment with a region delimiter to indicate that the
@ -122,7 +114,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
fragment(this);
CloseRegion();
}
ProfilingEnd();
}
/// <summary>
@ -133,12 +124,10 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value used by <paramref name="fragment"/>.</param>
public void AddContent<TValue>(int sequence, RenderFragment<TValue>? fragment, TValue value)
{
ProfilingStart();
if (fragment != null)
{
AddContent(sequence, fragment(value));
}
ProfilingEnd();
}
/// <summary>
@ -169,15 +158,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="name">The name of the attribute.</param>
public void AddAttribute(int sequence, string name)
{
ProfilingStart();
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element)
{
throw new InvalidOperationException($"Valueless attributes may only be added immediately after frames of type {RenderTreeFrameType.Element}");
}
Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue));
ProfilingEnd();
_entries.AppendAttribute(sequence, name, BoxedTrue);
}
/// <summary>
@ -194,23 +181,21 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, bool value)
{
ProfilingStart();
AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
Append(RenderTreeFrame.Attribute(sequence, name, value ? BoxedTrue : BoxedFalse));
_entries.AppendAttribute(sequence, name, value ? BoxedTrue : BoxedFalse);
}
else if (value)
{
// Don't add 'false' attributes for elements. We want booleans to map to the presence
// or absence of an attribute, and false => "False" which isn't falsy in js.
Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue));
_entries.AppendAttribute(sequence, name, BoxedTrue);
}
else
{
TrackAttributeName(name);
}
ProfilingEnd();
}
/// <summary>
@ -227,17 +212,15 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, string? value)
{
ProfilingStart();
AssertCanAddAttribute();
if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
Append(RenderTreeFrame.Attribute(sequence, name, value));
_entries.AppendAttribute(sequence, name, value);
}
else
{
TrackAttributeName(name);
}
ProfilingEnd();
}
/// <summary>
@ -254,17 +237,15 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, MulticastDelegate? value)
{
ProfilingStart();
AssertCanAddAttribute();
if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
Append(RenderTreeFrame.Attribute(sequence, name, value));
_entries.AppendAttribute(sequence, name, value);
}
else
{
TrackAttributeName(name);
}
ProfilingEnd();
}
/// <summary>
@ -285,32 +266,30 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </remarks>
public void AddAttribute(int sequence, string name, EventCallback value)
{
ProfilingStart();
AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
// Since this is a component, we need to preserve the type of the EventCallback, so we have
// to box.
Append(RenderTreeFrame.Attribute(sequence, name, (object)value));
_entries.AppendAttribute(sequence, name, value);
}
else if (value.RequiresExplicitReceiver)
{
// If we need to preserve the receiver, we just box the EventCallback
// so we can get it out on the other side.
Append(RenderTreeFrame.Attribute(sequence, name, (object)value));
_entries.AppendAttribute(sequence, name, value);
}
else if (value.HasDelegate)
{
// In the common case the receiver is also the delegate's target, so we
// just need to retain the delegate. This allows us to avoid an allocation.
Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate));
_entries.AppendAttribute(sequence, name, value.Delegate);
}
else
{
// Track the attribute name if needed since we elided the frame.
TrackAttributeName(name);
}
ProfilingEnd();
}
/// <summary>
@ -331,32 +310,30 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </remarks>
public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value)
{
ProfilingStart();
AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
// Since this is a component, we need to preserve the type of the EventCallback, so we have
// to box.
Append(RenderTreeFrame.Attribute(sequence, name, (object)value));
_entries.AppendAttribute(sequence, name, value);
}
else if (value.RequiresExplicitReceiver)
{
// If we need to preserve the receiver - we convert this to an untyped EventCallback. We don't
// need to preserve the type of an EventCallback<T> when it's invoked from the DOM.
Append(RenderTreeFrame.Attribute(sequence, name, (object)value.AsUntyped()));
_entries.AppendAttribute(sequence, name, value.AsUntyped());
}
else if (value.HasDelegate)
{
// In the common case the receiver is also the delegate's target, so we
// just need to retain the delegate. This allows us to avoid an allocation.
Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate));
_entries.AppendAttribute(sequence, name, value.Delegate);
}
else
{
// Track the attribute name if needed since we elided the frame.
TrackAttributeName(name);
}
ProfilingEnd();
}
/// <summary>
@ -370,7 +347,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, object? value)
{
ProfilingStart();
// This looks a bit daunting because we need to handle the boxed/object version of all of the
// types that AddAttribute special cases.
if (_lastNonAttributeFrameType == RenderTreeFrameType.Element)
@ -384,7 +360,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
if (boolValue)
{
Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue));
_entries.AppendAttribute(sequence, name, BoxedTrue);
}
else
{
@ -396,7 +372,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
if (callbackValue.HasDelegate)
{
Append(RenderTreeFrame.Attribute(sequence, name, callbackValue.UnpackForRenderTree()));
_entries.AppendAttribute(sequence, name, callbackValue.UnpackForRenderTree());
}
else
{
@ -405,25 +381,24 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
else if (value is MulticastDelegate)
{
Append(RenderTreeFrame.Attribute(sequence, name, value));
_entries.AppendAttribute(sequence, name, value);
}
else
{
// The value is either a string, or should be treated as a string.
Append(RenderTreeFrame.Attribute(sequence, name, value.ToString()));
_entries.AppendAttribute(sequence, name, value.ToString());
}
}
else if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
// If this is a component, we always want to preserve the original type.
Append(RenderTreeFrame.Attribute(sequence, name, value));
_entries.AppendAttribute(sequence, name, value);
}
else
{
// This is going to throw. Calling it just to get a consistent exception message.
AssertCanAddAttribute();
}
ProfilingEnd();
}
/// <summary>
@ -436,17 +411,16 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
/// <param name="frame">A <see cref="RenderTreeFrame"/> holding the name and value of the attribute.</param>
public void AddAttribute(int sequence, in RenderTreeFrame frame)
public void AddAttribute(int sequence, RenderTreeFrame frame)
{
ProfilingStart();
if (frame.FrameType != RenderTreeFrameType.Attribute)
if (frame.FrameTypeField != RenderTreeFrameType.Attribute)
{
throw new ArgumentException($"The {nameof(frame.FrameType)} must be {RenderTreeFrameType.Attribute}.");
}
AssertCanAddAttribute();
Append(frame.WithAttributeSequence(sequence));
ProfilingEnd();
frame.SequenceField = sequence;
_entries.Append(frame);
}
/// <summary>
@ -456,7 +430,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="attributes">A collection of key-value pairs representing attributes.</param>
public void AddMultipleAttributes(int sequence, IEnumerable<KeyValuePair<string, object>>? attributes)
{
ProfilingStart();
// Calling this up-front just to make sure we validate before mutating anything.
AssertCanAddAttribute();
@ -473,7 +446,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
AddAttribute(sequence, attribute.Key, attribute.Value);
}
}
ProfilingEnd();
}
/// <summary>
@ -490,20 +462,18 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="updatesAttributeName">The name of another attribute whose value can be updated when the event handler is executed.</param>
public void SetUpdatesAttributeName(string updatesAttributeName)
{
ProfilingStart();
if (_entries.Count == 0)
{
throw new InvalidOperationException("No preceding attribute frame exists.");
}
ref var prevFrame = ref _entries.Buffer[_entries.Count - 1];
if (prevFrame.FrameType != RenderTreeFrameType.Attribute)
if (prevFrame.FrameTypeField != RenderTreeFrameType.Attribute)
{
throw new InvalidOperationException($"Incorrect frame type: '{prevFrame.FrameType}'");
throw new InvalidOperationException($"Incorrect frame type: '{prevFrame.FrameTypeField}'");
}
prevFrame = prevFrame.WithAttributeEventUpdatesAttributeName(updatesAttributeName);
ProfilingEnd();
prevFrame.AttributeEventUpdatesAttributeNameField = updatesAttributeName;
}
/// <summary>
@ -535,12 +505,10 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="value">The value for the key.</param>
public void SetKey(object? value)
{
ProfilingStart();
if (value == null)
{
// Null is equivalent to not having set a key, which is valuable because Razor syntax doesn't have an
// easy way to have conditional directive attributes
ProfilingEnd();
return;
}
@ -552,23 +520,21 @@ namespace Microsoft.AspNetCore.Components.Rendering
var parentFrameIndexValue = parentFrameIndex.Value;
ref var parentFrame = ref _entries.Buffer[parentFrameIndexValue];
switch (parentFrame.FrameType)
switch (parentFrame.FrameTypeField)
{
case RenderTreeFrameType.Element:
parentFrame = parentFrame.WithElementKey(value); // It's a ref var, so this writes to the array
parentFrame.ElementKeyField = value; // It's a ref var, so this writes to the array
break;
case RenderTreeFrameType.Component:
parentFrame = parentFrame.WithComponentKey(value); // It's a ref var, so this writes to the array
parentFrame.ComponentKeyField = value; // It's a ref var, so this writes to the array
break;
default:
throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameType}.");
throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameTypeField}.");
}
ProfilingEnd();
}
private void OpenComponentUnchecked(int sequence, Type componentType)
{
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes)
@ -578,8 +544,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
_openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.ChildComponent(sequence, componentType));
ProfilingEnd();
_entries.AppendComponent(sequence, componentType);
_lastNonAttributeFrameType = RenderTreeFrameType.Component;
}
/// <summary>
@ -588,7 +554,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
public void CloseComponent()
{
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop();
// We might be closing a component with only attributes. Run the attribute cleanup pass
@ -598,9 +563,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1);
}
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
_entries.Buffer[indexOfEntryBeingClosed].ComponentSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed;
}
/// <summary>
@ -610,14 +573,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="elementReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param>
public void AddElementReferenceCapture(int sequence, Action<ElementReference> elementReferenceCaptureAction)
{
ProfilingStart();
if (GetCurrentParentFrameType() != RenderTreeFrameType.Element)
{
throw new InvalidOperationException($"Element reference captures may only be added as children of frames of type {RenderTreeFrameType.Element}");
}
Append(RenderTreeFrame.ElementReferenceCapture(sequence, elementReferenceCaptureAction));
ProfilingEnd();
_entries.AppendElementReferenceCapture(sequence, elementReferenceCaptureAction);
_lastNonAttributeFrameType = RenderTreeFrameType.ElementReferenceCapture;
}
/// <summary>
@ -627,7 +589,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="componentReferenceCaptureAction">An action to be invoked whenever the reference value changes.</param>
public void AddComponentReferenceCapture(int sequence, Action<object?> componentReferenceCaptureAction)
{
ProfilingStart();
var parentFrameIndex = GetCurrentParentFrameIndex();
if (!parentFrameIndex.HasValue)
{
@ -635,13 +596,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
var parentFrameIndexValue = parentFrameIndex.Value;
if (_entries.Buffer[parentFrameIndexValue].FrameType != RenderTreeFrameType.Component)
if (_entries.Buffer[parentFrameIndexValue].FrameTypeField != RenderTreeFrameType.Component)
{
throw new InvalidOperationException(ComponentReferenceCaptureInvalidParentMessage);
}
Append(RenderTreeFrame.ComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue));
ProfilingEnd();
_entries.AppendComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue);
_lastNonAttributeFrameType = RenderTreeFrameType.ComponentReferenceCapture;
}
/// <summary>
@ -650,7 +611,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
public void OpenRegion(int sequence)
{
ProfilingStart();
// We are entering a new scope, since we track the "duplicate attributes" per
// element/component we might need to clean them up now.
if (_hasSeenAddMultipleAttributes)
@ -660,8 +620,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
_openElementIndices.Push(_entries.Count);
Append(RenderTreeFrame.Region(sequence));
ProfilingEnd();
_entries.AppendRegion(sequence);
_lastNonAttributeFrameType = RenderTreeFrameType.Region;
}
/// <summary>
@ -670,11 +630,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
public void CloseRegion()
{
ProfilingStart();
var indexOfEntryBeingClosed = _openElementIndices.Pop();
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
ProfilingEnd();
_entries.Buffer[indexOfEntryBeingClosed].RegionSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed;
}
private void AssertCanAddAttribute()
@ -693,7 +650,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
var parentIndex = GetCurrentParentFrameIndex();
return parentIndex.HasValue
? _entries.Buffer[parentIndex.Value].FrameType
? _entries.Buffer[parentIndex.Value].FrameTypeField
: (RenderTreeFrameType?)null;
}
@ -702,29 +659,24 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
public void Clear()
{
ProfilingStart();
_entries.Clear();
_openElementIndices.Clear();
_lastNonAttributeFrameType = null;
_hasSeenAddMultipleAttributes = false;
_seenAttributeNames?.Clear();
ProfilingEnd();
}
// internal because this should only be used during the post-event tree patching logic
// It's expensive because it involves copying all the subsequent memory in the array
internal void InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue)
{
ProfilingStart();
// Replicate the same attribute omission logic as used elsewhere
if ((attributeValue == null) || (attributeValue is bool boolValue && !boolValue))
{
ProfilingEnd();
return;
}
_entries.InsertExpensive(insertAtIndex, RenderTreeFrame.Attribute(sequence, attributeName, attributeValue));
ProfilingEnd();
}
/// <summary>
@ -734,21 +686,9 @@ namespace Microsoft.AspNetCore.Components.Rendering
public ArrayRange<RenderTreeFrame> GetFrames() =>
_entries.ToRange();
private void Append(in RenderTreeFrame frame)
{
var frameType = frame.FrameType;
_entries.Append(frame);
if (frameType != RenderTreeFrameType.Attribute)
{
_lastNonAttributeFrameType = frame.FrameType;
}
}
// Internal for testing
internal void ProcessDuplicateAttributes(int first)
{
ProfilingStart();
Debug.Assert(_hasSeenAddMultipleAttributes);
// When AddMultipleAttributes method has been called, we need to postprocess attributes while closing
@ -759,7 +699,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
for (var i = first; i <= last; i++)
{
if (buffer[i].FrameType != RenderTreeFrameType.Attribute)
if (buffer[i].FrameTypeField != RenderTreeFrameType.Attribute)
{
last = i - 1;
break;
@ -771,12 +711,12 @@ namespace Microsoft.AspNetCore.Components.Rendering
for (var i = last; i >= first; i--)
{
ref var frame = ref buffer[i];
Debug.Assert(frame.FrameType == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameType} at {i}");
Debug.Assert(frame.FrameTypeField == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameTypeField} at {i}");
if (!seenAttributeNames.TryGetValue(frame.AttributeName, out var index))
if (!seenAttributeNames.TryGetValue(frame.AttributeNameField, out var index))
{
// This is the first time seeing this attribute name. Add to the dictionary and move on.
seenAttributeNames.Add(frame.AttributeName, i);
seenAttributeNames.Add(frame.AttributeNameField, i);
}
else if (index < i)
{
@ -784,7 +724,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
// This is the case for a null event handler, or bool false value.
//
// We need to update our tracking, in case the attribute appeared 3 or more times.
seenAttributeNames[frame.AttributeName] = i;
seenAttributeNames[frame.AttributeNameField] = i;
}
else if (index > i)
{
@ -815,7 +755,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
for (var i = first; i < _entries.Count; i++)
{
ref var frame = ref buffer[i];
if (frame.FrameType != RenderTreeFrameType.None)
if (frame.FrameTypeField != RenderTreeFrameType.None)
{
buffer[offset++] = frame;
}
@ -830,7 +770,6 @@ namespace Microsoft.AspNetCore.Components.Rendering
seenAttributeNames.Clear();
_hasSeenAddMultipleAttributes = false;
ProfilingEnd();
}
// Internal for testing
@ -847,22 +786,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
void IDisposable.Dispose()
{
ProfilingStart();
_entries.Dispose();
ProfilingEnd();
}
// Having too many calls to ComponentsProfiling.Instance.Start/End has a measurable perf impact
// even when capturing is disabled. So, to enable detailed profiling for this class, define the
// Profile_RenderTreeBuilder compiler symbol, otherwise the calls are compiled out entirely.
// Enabling detailed profiling adds about 5% to rendering benchmark times.
[Conditional("Profile_RenderTreeBuilder")]
private static void ProfilingStart([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.Start(name);
[Conditional("Profile_RenderTreeBuilder")]
private static void ProfilingEnd([CallerMemberName] string? name = null)
=> ComponentsProfiling.Instance.End(name);
}
}

View File

@ -24,21 +24,21 @@ namespace Microsoft.AspNetCore.Components.Rendering
for (var frameIndex = 0; frameIndex < framesLength; frameIndex++)
{
ref var frame = ref framesArray[frameIndex];
switch (frame.FrameType)
switch (frame.FrameTypeField)
{
case RenderTreeFrameType.Element:
closestElementFrameIndex = frameIndex;
break;
case RenderTreeFrameType.Attribute:
if (frame.AttributeEventHandlerId == eventHandlerId)
if (frame.AttributeEventHandlerIdField == eventHandlerId)
{
if (!string.IsNullOrEmpty(frame.AttributeEventUpdatesAttributeName))
if (!string.IsNullOrEmpty(frame.AttributeEventUpdatesAttributeNameField))
{
UpdateFrameToMatchClientState(
renderTreeBuilder,
framesArray,
closestElementFrameIndex,
frame.AttributeEventUpdatesAttributeName,
frame.AttributeEventUpdatesAttributeNameField,
newFieldValue);
}
@ -55,20 +55,20 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
// Find the attribute frame
ref var elementFrame = ref framesArray[elementFrameIndex];
var elementSubtreeEndIndexExcl = elementFrameIndex + elementFrame.ElementSubtreeLength;
var elementSubtreeEndIndexExcl = elementFrameIndex + elementFrame.ElementSubtreeLengthField;
for (var attributeFrameIndex = elementFrameIndex + 1; attributeFrameIndex < elementSubtreeEndIndexExcl; attributeFrameIndex++)
{
ref var attributeFrame = ref framesArray[attributeFrameIndex];
if (attributeFrame.FrameType != RenderTreeFrameType.Attribute)
if (attributeFrame.FrameTypeField != RenderTreeFrameType.Attribute)
{
// We're now looking at the descendants not attributes, so the search is over
break;
}
if (attributeFrame.AttributeName == attributeName)
if (attributeFrame.AttributeNameField == attributeName)
{
// Found an existing attribute we can update
attributeFrame = attributeFrame.WithAttributeValue(attributeValue);
attributeFrame.AttributeValueField = attributeValue;
return;
}
}
@ -84,25 +84,25 @@ namespace Microsoft.AspNetCore.Components.Rendering
for (var otherFrameIndex = elementFrameIndex; otherFrameIndex >= 0; otherFrameIndex--)
{
ref var otherFrame = ref framesArray[otherFrameIndex];
switch (otherFrame.FrameType)
switch (otherFrame.FrameTypeField)
{
case RenderTreeFrameType.Element:
{
var otherFrameSubtreeLength = otherFrame.ElementSubtreeLength;
var otherFrameSubtreeLength = otherFrame.ElementSubtreeLengthField;
var otherFrameEndIndexExcl = otherFrameIndex + otherFrameSubtreeLength;
if (otherFrameEndIndexExcl > elementFrameIndex) // i.e., contains the element we're inserting into
{
otherFrame = otherFrame.WithElementSubtreeLength(otherFrameSubtreeLength + 1);
otherFrame.ElementSubtreeLengthField = otherFrameSubtreeLength + 1;
}
break;
}
case RenderTreeFrameType.Region:
{
var otherFrameSubtreeLength = otherFrame.RegionSubtreeLength;
var otherFrameSubtreeLength = otherFrame.RegionSubtreeLengthField;
var otherFrameEndIndexExcl = otherFrameIndex + otherFrameSubtreeLength;
if (otherFrameEndIndexExcl > elementFrameIndex) // i.e., contains the element we're inserting into
{
otherFrame = otherFrame.WithRegionSubtreeLength(otherFrameSubtreeLength + 1);
otherFrame.RegionSubtreeLengthField = otherFrameSubtreeLength + 1;
}
break;
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.Routing
public override bool Match(string pathSegment, out object? convertedValue)
{
// Unset values are set to null in the Parameters object created in
// the RouteContext. To match this pattern, unset optional parmeters
// the RouteContext. To match this pattern, unset optional parameters
// are converted to null.
if (string.IsNullOrEmpty(pathSegment))
{

View File

@ -29,10 +29,20 @@ namespace Microsoft.AspNetCore.Components.Routing
internal void Match(RouteContext context)
{
string? catchAllValue = null;
// If this template contains a catch-all parameter, we can concatenate the pathSegments
// at and beyond the catch-all segment's position. For example:
// Template: /foo/bar/{*catchAll}
// PathSegments: /foo/bar/one/two/three
if (Template.ContainsCatchAllSegment && context.Segments.Length >= Template.Segments.Length)
{
catchAllValue = string.Join('/', context.Segments[Range.StartAt(Template.Segments.Length - 1)]);
}
// If there are no optional segments on the route and the length of the route
// and the template do not match, then there is no chance of this matching and
// we can bail early.
if (Template.OptionalSegmentsCount == 0 && Template.Segments.Length != context.Segments.Length)
else if (Template.OptionalSegmentsCount == 0 && Template.Segments.Length != context.Segments.Length)
{
return;
}
@ -43,7 +53,15 @@ namespace Microsoft.AspNetCore.Components.Routing
for (var i = 0; i < Template.Segments.Length; i++)
{
var segment = Template.Segments[i];
if (segment.IsCatchAll)
{
numMatchingSegments += 1;
parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
parameters[segment.Value] = catchAllValue;
break;
}
// If the template contains more segments than the path, then
// we may need to break out of this for-loop. This can happen
// in one of two cases:
@ -86,7 +104,7 @@ namespace Microsoft.AspNetCore.Components.Routing
// In addition to extracting parameter values from the URL, each route entry
// also knows which other parameters should be supplied with null values. These
// are parameters supplied by other route entries matching the same handler.
if (UnusedRouteParameterNames.Length > 0)
if (!Template.ContainsCatchAllSegment && UnusedRouteParameterNames.Length > 0)
{
parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
for (var i = 0; i < UnusedRouteParameterNames.Length; i++)
@ -116,7 +134,7 @@ namespace Microsoft.AspNetCore.Components.Routing
// `/this/is/a/template` and the route `/this/`. In that case, we want to ensure
// that all non-optional segments have matched as well.
var allNonOptionalSegmentsMatch = numMatchingSegments >= (Template.Segments.Length - Template.OptionalSegmentsCount);
if (allRouteSegmentsMatch && allNonOptionalSegmentsMatch)
if (Template.ContainsCatchAllSegment || (allRouteSegmentsMatch && allNonOptionalSegmentsMatch))
{
context.Parameters = parameters;
context.Handler = Handler;

View File

@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Components.Routing
TemplateText = templateText;
Segments = segments;
OptionalSegmentsCount = segments.Count(template => template.IsOptional);
ContainsCatchAllSegment = segments.Any(template => template.IsCatchAll);
}
public string TemplateText { get; }
@ -22,5 +23,7 @@ namespace Microsoft.AspNetCore.Components.Routing
public TemplateSegment[] Segments { get; }
public int OptionalSegmentsCount { get; }
public bool ContainsCatchAllSegment { get; }
}
}

View File

@ -152,6 +152,19 @@ namespace Microsoft.AspNetCore.Components.Routing
internal virtual void Refresh(bool isNavigationIntercepted)
{
// If an `OnNavigateAsync` task is currently in progress, then wait
// for it to complete before rendering. Note: because _previousOnNavigateTask
// is initialized to a CompletedTask on initialization, this will still
// allow first-render to complete successfully.
if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion)
{
if (Navigating != null)
{
_renderHandle.Render(Navigating);
}
return;
}
RefreshRouteTable();
var locationPath = NavigationManager.ToBaseRelativePath(_locationAbsolute);
@ -248,19 +261,15 @@ namespace Microsoft.AspNetCore.Components.Routing
var previousTask = _previousOnNavigateTask;
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_previousOnNavigateTask = tcs.Task;
try
// And pass an indicator for the previous task to the currently running one.
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
tcs.SetResult();
if (shouldRefresh)
{
// And pass an indicator for the previous task to the currently running one.
var shouldRefresh = await RunOnNavigateAsync(path, previousTask);
if (shouldRefresh)
{
Refresh(isNavigationIntercepted);
}
}
finally
{
tcs.SetResult();
Refresh(isNavigationIntercepted);
}
}
private void OnLocationChanged(object sender, LocationChangedEventArgs args)

View File

@ -12,15 +12,15 @@ namespace Microsoft.AspNetCore.Components.Routing
// The class in here just takes care of parsing a route and extracting
// simple parameters from it.
// Some differences with ASP.NET Core routes are:
// * We don't support catch all parameter segments.
// * We don't support complex segments.
// The things that we support are:
// * Literal path segments. (Like /Path/To/Some/Page)
// * Parameter path segments (Like /Customer/{Id}/Orders/{OrderId})
// * Catch-all parameters (Like /blog/{*slug})
internal class TemplateParser
{
public static readonly char[] InvalidParameterNameCharacters =
new char[] { '*', '{', '}', '=', '.' };
new char[] { '{', '}', '=', '.' };
internal static RouteTemplate ParseTemplate(string template)
{
@ -80,6 +80,12 @@ namespace Microsoft.AspNetCore.Components.Routing
for (int i = 0; i < templateSegments.Length; i++)
{
var currentSegment = templateSegments[i];
if (currentSegment.IsCatchAll && i != templateSegments.Length - 1)
{
throw new InvalidOperationException($"Invalid template '{template}'. A catch-all parameter can only appear as the last segment of the route template.");
}
if (!currentSegment.IsParameter)
{
continue;

View File

@ -12,34 +12,48 @@ namespace Microsoft.AspNetCore.Components.Routing
{
IsParameter = isParameter;
IsCatchAll = segment.StartsWith('*');
if (IsCatchAll)
{
// Only one '*' currently allowed
Value = segment.Substring(1);
var invalidCharacter = Value.IndexOf('*');
if (Value.IndexOf('*') != -1)
{
throw new InvalidOperationException($"Invalid template '{template}'. A catch-all parameter may only have one '*' at the beginning of the segment.");
}
}
else
{
Value = segment;
}
// Process segments that are not parameters or do not contain
// a token separating a type constraint.
if (!isParameter || segment.IndexOf(':') < 0)
if (!isParameter || Value.IndexOf(':') < 0)
{
// Set the IsOptional flag to true for segments that contain
// a parameter with no type constraints but optionality set
// via the '?' token.
if (segment.IndexOf('?') == segment.Length - 1)
if (Value.IndexOf('?') == Value.Length - 1)
{
IsOptional = true;
Value = segment.Substring(0, segment.Length - 1);
Value = Value.Substring(0, Value.Length - 1);
}
// If the `?` optional marker shows up in the segment but not at the very end,
// then throw an error.
else if (segment.IndexOf('?') >= 0 && segment.IndexOf('?') != segment.Length - 1)
else if (Value.IndexOf('?') >= 0 && Value.IndexOf('?') != Value.Length - 1)
{
throw new ArgumentException($"Malformed parameter '{segment}' in route '{template}'. '?' character can only appear at the end of parameter name.");
}
else
{
Value = segment;
}
Constraints = Array.Empty<RouteConstraint>();
}
else
{
var tokens = segment.Split(':');
var tokens = Value.Split(':');
if (tokens[0].Length == 0)
{
throw new ArgumentException($"Malformed parameter '{segment}' in route '{template}' has no name before the constraints list.");
@ -54,6 +68,21 @@ namespace Microsoft.AspNetCore.Components.Routing
.Select(token => RouteConstraint.Parse(template, segment, token))
.ToArray();
}
if (IsParameter)
{
if (IsOptional && IsCatchAll)
{
throw new InvalidOperationException($"Invalid segment '{segment}' in route '{template}'. A catch-all parameter cannot be marked optional.");
}
// Moving the check for this here instead of TemplateParser so we can allow catch-all.
// We checked for '*' up above specifically for catch-all segments, this one checks for all others
if (Value.IndexOf('*') != -1)
{
throw new InvalidOperationException($"Invalid template '{template}'. The character '*' in parameter segment '{{{segment}}}' is not allowed.");
}
}
}
// The value of the segment. The exact text to match when is a literal.
@ -64,6 +93,8 @@ namespace Microsoft.AspNetCore.Components.Routing
public bool IsOptional { get; }
public bool IsCatchAll { get; }
public RouteConstraint[] Constraints { get; }
public bool Match(string pathSegment, out object? matchedParameterValue)

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Components
var callback = default(EventCallback);
// Act & Assert (Does not throw)
await callback.InvokeAsync(null);
await callback.InvokeAsync();
}
[Fact]
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Components
var callback = default(EventCallback<EventArgs>);
// Act & Assert (Does not throw)
await callback.InvokeAsync(null);
await callback.InvokeAsync();
}
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback(null, (Action)(() => runCount++));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback<EventArgs>(null, (Action)(() => runCount++));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback(component, (Action)(() => runCount++));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback(component, (Action<EventArgs>)((e) => { arg = e; runCount++; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -184,7 +184,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback(component, (Func<Task>)(() => { runCount++; return Task.CompletedTask; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback(component, (Func<EventArgs, Task>)((e) => { arg = e; runCount++; return Task.CompletedTask; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -297,7 +297,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback<EventArgs>(component, (Action)(() => runCount++));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -334,7 +334,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback<EventArgs>(component, (Action<EventArgs>)((e) => { arg = e; runCount++; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback<EventArgs>(component, (Func<Task>)(() => { runCount++; return Task.CompletedTask; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert
@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Components
var callback = new EventCallback<EventArgs>(component, (Func<EventArgs, Task>)((e) => { arg = e; runCount++; return Task.CompletedTask; }));
// Act
await callback.InvokeAsync(null);
await callback.InvokeAsync();
// Assert

View File

@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components
}
[Fact]
public void IncomingParameterMatchesOverridenParameter_ThatDoesNotHasAttribute()
public void IncomingParameterMatchesOverriddenParameter_ThatDoesNotHaveAttribute()
{
// Test for https://github.com/dotnet/aspnetcore/issues/13162
// Arrange

View File

@ -340,7 +340,7 @@ namespace Microsoft.AspNetCore.Components.Test
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(() => GetSingleUpdatedComponent());
Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message);
Assert.Equal("More than one sibling of element 'el' has the same key value, 'key1'. Key values must be unique.", ex.Message);
}
[Fact]
@ -357,7 +357,7 @@ namespace Microsoft.AspNetCore.Components.Test
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(() => GetSingleUpdatedComponent());
Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message);
Assert.Equal("More than one sibling of element 'el' has the same key value, 'key1'. Key values must be unique.", ex.Message);
}
[Fact]
@ -374,7 +374,7 @@ namespace Microsoft.AspNetCore.Components.Test
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(() => GetSingleUpdatedComponent());
Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message);
Assert.Equal("More than one sibling of element 'el' has the same key value, 'key1'. Key values must be unique.", ex.Message);
}
[Fact]

View File

@ -487,7 +487,8 @@ namespace Microsoft.AspNetCore.Components.Test
public void DispatchEventHandlesSynchronousExceptionsFromEventHandlers()
{
// Arrange: Render a component with an event handler
var renderer = new TestRenderer {
var renderer = new TestRenderer
{
ShouldHandleExceptions = true
};
@ -2086,6 +2087,238 @@ namespace Microsoft.AspNetCore.Components.Test
Assert.Contains(exception2, aex.InnerExceptions);
}
[Fact]
public void RenderBatch_HandlesSynchronousExceptionsInAsyncDisposableComponents()
{
// Arrange
var renderer = new TestRenderer { ShouldHandleExceptions = true };
var exception1 = new InvalidOperationException();
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(1, nameof(AsyncDisposableComponent.AsyncDisposeAction), (Func<ValueTask>)(() => throw exception1));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
var aex = Assert.IsType<AggregateException>(Assert.Single(renderer.HandledExceptions));
var innerException = Assert.Single(aex.Flatten().InnerExceptions);
Assert.Same(exception1, innerException);
}
[Fact]
public void RenderBatch_CanDisposeSynchronousAsyncDisposableImplementations()
{
// Arrange
var renderer = new TestRenderer { ShouldHandleExceptions = true };
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(1, nameof(AsyncDisposableComponent.AsyncDisposeAction), (Func<ValueTask>)(() => default));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
Assert.Empty(renderer.HandledExceptions);
}
[Fact]
public void RenderBatch_CanDisposeAsynchronousAsyncDisposables()
{
// Arrange
var semaphore = new Semaphore(0, 1);
var renderer = new TestRenderer { ShouldHandleExceptions = true };
renderer.OnExceptionHandled = () => semaphore.Release();
var exception1 = new InvalidOperationException();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(1, nameof(AsyncDisposableComponent.AsyncDisposeAction), (Func<ValueTask>)(async () => { await tcs.Task; }));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
Assert.Empty(renderer.HandledExceptions);
// Continue execution
tcs.SetResult();
Assert.False(semaphore.WaitOne(10));
Assert.Empty(renderer.HandledExceptions);
}
[Fact]
public void RenderBatch_HandlesAsynchronousExceptionsInAsyncDisposableComponents()
{
// Arrange
var semaphore = new Semaphore(0, 1);
var renderer = new TestRenderer { ShouldHandleExceptions = true };
renderer.OnExceptionHandled = () => semaphore.Release();
var exception1 = new InvalidOperationException();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(1, nameof(AsyncDisposableComponent.AsyncDisposeAction), (Func<ValueTask>)(async () => { await tcs.Task; throw exception1; }));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
Assert.Empty(renderer.HandledExceptions);
// Continue execution
tcs.SetResult();
semaphore.WaitOne();
var aex = Assert.IsType<InvalidOperationException>(Assert.Single(renderer.HandledExceptions));
Assert.Same(exception1, aex);
}
[Fact]
public void RenderBatch_ReportsSynchronousCancelationsAsErrors()
{
// Arrange
var renderer = new TestRenderer { ShouldHandleExceptions = true };
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(1, nameof(AsyncDisposableComponent.AsyncDisposeAction), (Func<ValueTask>)(() => throw new TaskCanceledException()));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
var aex = Assert.IsType<AggregateException>(Assert.Single(renderer.HandledExceptions));
Assert.IsType<TaskCanceledException>(Assert.Single(aex.Flatten().InnerExceptions));
}
[Fact]
public void RenderBatch_ReportsAsynchronousCancelationsAsErrors()
{
// Arrange
var semaphore = new Semaphore(0, 1);
var renderer = new TestRenderer { ShouldHandleExceptions = true };
renderer.OnExceptionHandled += () => semaphore.Release();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var firstRender = true;
var component = new TestComponent(builder =>
{
if (firstRender)
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AsyncDisposableComponent>(1);
builder.AddAttribute(
1,
nameof(AsyncDisposableComponent.AsyncDisposeAction),
(Func<ValueTask>)(() => new ValueTask(tcs.Task)));
builder.CloseComponent();
}
});
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
// Act: Second render
firstRender = false;
component.TriggerRender();
// Assert: Applicable children are included in disposal list
Assert.Equal(2, renderer.Batches.Count);
Assert.Equal(new[] { 1, }, renderer.Batches[1].DisposedComponentIDs);
// Outer component is still alive and not disposed.
Assert.False(component.Disposed);
Assert.Empty(renderer.HandledExceptions);
// Cancel execution
tcs.SetCanceled();
semaphore.WaitOne();
var aex = Assert.IsType<TaskCanceledException>(Assert.Single(renderer.HandledExceptions));
}
[Fact]
public void RenderBatch_DoesNotDisposeComponentMultipleTimes()
{
@ -2589,7 +2822,7 @@ namespace Microsoft.AspNetCore.Components.Test
// Act: Toggle the checkbox
var eventArgs = new ChangeEventArgs { Value = true };
var renderTask = renderer.DispatchEventAsync(checkboxChangeEventHandlerId, eventArgs);
var renderTask = renderer.DispatchEventAsync(checkboxChangeEventHandlerId, eventArgs);
Assert.True(renderTask.IsCompletedSuccessfully);
var latestBatch = renderer.Batches.Last();
@ -3768,7 +4001,7 @@ namespace Microsoft.AspNetCore.Components.Test
requestedType => Assert.Equal(typeof(TestComponent), requestedType));
}
private class TestComponentActivator<TResult> : IComponentActivator where TResult: IComponent, new()
private class TestComponentActivator<TResult> : IComponentActivator where TResult : IComponent, new()
{
public List<Type> RequestedComponentTypes { get; } = new List<Type>();
@ -4147,6 +4380,24 @@ namespace Microsoft.AspNetCore.Components.Test
}
}
private class AsyncDisposableComponent : AutoRenderComponent, IAsyncDisposable
{
public bool Disposed { get; private set; }
[Parameter]
public Func<ValueTask> AsyncDisposeAction { get; set; }
public ValueTask DisposeAsync()
{
Disposed = true;
return AsyncDisposeAction == null ? default : AsyncDisposeAction.Invoke();
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
}
}
class TestAsyncRenderer : TestRenderer
{
public Task NextUpdateDisplayReturnTask { get; set; }

View File

@ -226,6 +226,23 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
Assert.Single(context.Parameters, p => p.Key == "parameter" && (string)p.Value == expectedValue);
}
[Theory]
[InlineData("/blog/value1", "value1")]
[InlineData("/blog/value1/foo%20bar", "value1/foo bar")]
public void CanMatchCatchAllParameterTemplate(string path, string expectedValue)
{
// Arrange
var routeTable = new TestRouteTableBuilder().AddRoute("/blog/{*parameter}").Build();
var context = new RouteContext(path);
// Act
routeTable.Route(context);
// Assert
Assert.NotNull(context.Handler);
Assert.Single(context.Parameters, p => p.Key == "parameter" && (string)p.Value == expectedValue);
}
[Fact]
public void CanMatchTemplateWithMultipleParameters()
{
@ -247,6 +264,29 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
Assert.Equal(expectedParameters, context.Parameters);
}
[Fact]
public void CanMatchTemplateWithMultipleParametersAndCatchAllParameter()
{
// Arrange
var routeTable = new TestRouteTableBuilder().AddRoute("/{some}/awesome/{route}/with/{*catchAll}").Build();
var context = new RouteContext("/an/awesome/path/with/some/catch/all/stuff");
var expectedParameters = new Dictionary<string, object>
{
["some"] = "an",
["route"] = "path",
["catchAll"] = "some/catch/all/stuff"
};
// Act
routeTable.Route(context);
// Assert
Assert.NotNull(context.Handler);
Assert.Equal(expectedParameters, context.Parameters);
}
public static IEnumerable<object[]> CanMatchParameterWithConstraintCases() => new object[][]
{
new object[] { "/{value:bool}", "/true", true },
@ -476,7 +516,7 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
[Fact]
public void PrefersLiteralTemplateOverParmeterizedTemplates()
public void PrefersLiteralTemplateOverParameterizedTemplates()
{
// Arrange
var routeTable = new TestRouteTableBuilder()

View File

@ -83,6 +83,45 @@ namespace Microsoft.AspNetCore.Components.Routing
Assert.Equal(expected, actual, RouteTemplateTestComparer.Instance);
}
[Fact]
public void Parse_SingleCatchAllParameter()
{
// Arrange
var expected = new ExpectedTemplateBuilder().Parameter("p");
// Act
var actual = TemplateParser.ParseTemplate("{*p}");
// Assert
Assert.Equal(expected, actual, RouteTemplateTestComparer.Instance);
}
[Fact]
public void Parse_MixedLiteralAndCatchAllParameter()
{
// Arrange
var expected = new ExpectedTemplateBuilder().Literal("awesome").Literal("wow").Parameter("p");
// Act
var actual = TemplateParser.ParseTemplate("awesome/wow/{*p}");
// Assert
Assert.Equal(expected, actual, RouteTemplateTestComparer.Instance);
}
[Fact]
public void Parse_MixedLiteralParameterAndCatchAllParameter()
{
// Arrange
var expected = new ExpectedTemplateBuilder().Literal("awesome").Parameter("p1").Parameter("p2");
// Act
var actual = TemplateParser.ParseTemplate("awesome/{p1}/{*p2}");
// Assert
Assert.Equal(expected, actual, RouteTemplateTestComparer.Instance);
}
[Fact]
public void InvalidTemplate_WithRepeatedParameter()
{
@ -113,7 +152,8 @@ namespace Microsoft.AspNetCore.Components.Routing
}
[Theory]
[InlineData("{*}", "Invalid template '{*}'. The character '*' in parameter segment '{*}' is not allowed.")]
// * is only allowed at beginning for catch-all parameters
[InlineData("{p*}", "Invalid template '{p*}'. The character '*' in parameter segment '{p*}' is not allowed.")]
[InlineData("{{}", "Invalid template '{{}'. The character '{' in parameter segment '{{}' is not allowed.")]
[InlineData("{}}", "Invalid template '{}}'. The character '}' in parameter segment '{}}' is not allowed.")]
[InlineData("{=}", "Invalid template '{=}'. The character '=' in parameter segment '{=}' is not allowed.")]
@ -166,6 +206,26 @@ namespace Microsoft.AspNetCore.Components.Routing
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public void InvalidTemplate_CatchAllParamWithMultipleAsterisks()
{
var ex = Assert.Throws<InvalidOperationException>(() => TemplateParser.ParseTemplate("/test/{a}/{**b}"));
var expectedMessage = "Invalid template '/test/{a}/{**b}'. A catch-all parameter may only have one '*' at the beginning of the segment.";
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public void InvalidTemplate_CatchAllParamNotLast()
{
var ex = Assert.Throws<InvalidOperationException>(() => TemplateParser.ParseTemplate("/test/{*a}/{b}"));
var expectedMessage = "Invalid template 'test/{*a}/{b}'. A catch-all parameter can only appear as the last segment of the route template.";
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public void InvalidTemplate_BadOptionalCharacterPosition()
{

View File

@ -37,6 +37,7 @@
"src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj",
"src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
"src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\ConsoleHost\\Wasm.Performance.ConsoleHost.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\TestApp\\Wasm.Performance.TestApp.csproj",
"src\\Components\\test\\E2ETest\\Microsoft.AspNetCore.Components.E2ETests.csproj",

View File

@ -24,6 +24,7 @@ app {
height: 3.5rem;
display: flex;
align-items: center;
z-index: 10;
}
.main {

View File

@ -296,7 +296,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
exceptions.Add(e);
};
// Receive the ack for the intial batch
// Receive the ack for the initial batch
_ = renderer.OnRenderCompletedAsync(2, null);
// Receive the ack for the second batch
_ = renderer.OnRenderCompletedAsync(2, null);

View File

@ -28,8 +28,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
internal class ArrayBuilder<T> : IDisposable
{
// The following fields are memory mapped to the WASM client. Do not re-order or use auto-properties.
private T[] _items;
private int _itemsInUse;
protected T[] _items;
protected int _itemsInUse;
private static readonly T[] Empty = Array.Empty<T>();
private readonly ArrayPool<T> _arrayPool;
@ -139,7 +139,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree
ThrowIndexOutOfBoundsException();
}
// Same expansion logic as elsewhere
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
@ -162,7 +161,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
_itemsInUse = 0;
}
private void GrowBuffer(int desiredCapacity)
protected void GrowBuffer(int desiredCapacity)
{
// When we dispose, we set the count back to zero and return the array.
//

View File

@ -71,6 +71,7 @@ namespace Microsoft.AspNetCore.Components.Web
"touch" => Deserialize<TouchEventArgs>(eventArgsJson),
"unknown" => EventArgs.Empty,
"wheel" => Deserialize<WheelEventArgs>(eventArgsJson),
"toggle" => Deserialize<EventArgs>(eventArgsJson),
_ => throw new InvalidOperationException($"Unsupported event type '{eventArgsType}'. EventId: '{eventHandlerId}'."),
};
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,6 @@ import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions';
import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler';
import { attachRootComponentToLogicalElement } from './Rendering/Renderer';
import { initializeProfiling } from './Platform/Profiling';
let renderingFailed = false;
let started = false;
@ -22,7 +21,6 @@ async function boot(userOptions?: Partial<CircuitStartOptions>): Promise<void> {
throw new Error('Blazor has already started.');
}
started = true;
initializeProfiling(null);
// Establish options to be used
const options = resolveOptions(userOptions);

View File

@ -11,7 +11,6 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader';
import { BootConfigResult } from './Platform/BootConfig';
import { Pointer } from './Platform/Platform';
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
import { profileStart, profileEnd } from './Platform/Profiling';
let started = false;
@ -34,8 +33,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
const platform = Environment.setPlatform(monoPlatform);
window['Blazor'].platform = platform;
window['Blazor']._internal.renderBatch = (browserRendererId: number, batchAddress: Pointer) => {
profileStart('renderBatch');
// We're going to read directly from the .NET memory heap, so indicate to the platform
// that we don't want anything to modify the memory contents during this time. Currently this
// is only guaranteed by the fact that .NET code doesn't run during this time, but in the
@ -47,8 +44,6 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
} finally {
heapLock.release();
}
profileEnd('renderBatch');
};
// Configure navigation via JS Interop
@ -66,8 +61,11 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
);
});
// Get the custom environment setting if defined
const environment = options?.environment;
// Fetch the resources and prepare the Mono runtime
const bootConfigResult = await BootConfigResult.initAsync();
const bootConfigResult = await BootConfigResult.initAsync(environment);
const [resourceLoader] = await Promise.all([
WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, options || {}),

View File

@ -1,7 +1,7 @@
import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager';
import { attachRootComponentToElement } from './Rendering/Renderer';
import { domFunctions } from './DomWrapper';
import { setProfilingEnabled } from './Platform/Profiling';
import { Virtualize } from './Virtualize';
// Make the following APIs available in global scope for invocation from JS
window['Blazor'] = {
@ -11,6 +11,6 @@ window['Blazor'] = {
attachRootComponentToElement,
navigationManager: navigationManagerInternalFunctions,
domWrapper: domFunctions,
setProfilingEnabled: setProfilingEnabled,
Virtualize,
},
};

View File

@ -2,7 +2,7 @@ export class BootConfigResult {
private constructor(public bootConfig: BootJsonData, public applicationEnvironment: string) {
}
static async initAsync(): Promise<BootConfigResult> {
static async initAsync(environment?: string): Promise<BootConfigResult> {
const bootConfigResponse = await fetch('_framework/blazor.boot.json', {
method: 'GET',
credentials: 'include',
@ -10,8 +10,8 @@ export class BootConfigResult {
});
// While we can expect an ASP.NET Core hosted application to include the environment, other
// hosts may not. Assume 'Production' in the absenc of any specified value.
const applicationEnvironment = bootConfigResponse.headers.get('Blazor-Environment') || 'Production';
// hosts may not. Assume 'Production' in the absence of any specified value.
const applicationEnvironment = environment || bootConfigResponse.headers.get('Blazor-Environment') || 'Production';
const bootConfig: BootJsonData = await bootConfigResponse.json();
return new BootConfigResult(bootConfig, applicationEnvironment);

View File

@ -64,6 +64,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
this.document.body.appendChild(this.modal);
}
this.modal.style.display = 'block';
this.modal.classList.add('show');
this.button.style.display = 'none';
this.reloadParagraph.style.display = 'none';
this.message.textContent = 'Attempting to reconnect to the server...';
@ -71,6 +72,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
hide(): void {
this.modal.style.display = 'none';
this.modal.classList.remove('show');
}
failed(): void {

View File

@ -5,7 +5,6 @@ import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResour
import { Platform, System_Array, Pointer, System_Object, System_String, HeapLock } from '../Platform';
import { loadTimezoneData } from './TimezoneDataFile';
import { WebAssemblyBootResourceType } from '../WebAssemblyStartOptions';
import { initializeProfiling } from '../Profiling';
let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void;
const appBinDirName = 'appBinDir';
@ -36,10 +35,6 @@ export const monoPlatform: Platform = {
start: function start(resourceLoader: WebAssemblyResourceLoader) {
return new Promise<void>((resolve, reject) => {
attachDebuggerHotkey(resourceLoader);
initializeProfiling(isCapturing => {
const setCapturingMethod = bindStaticMethod('Microsoft.AspNetCore.Components', 'Microsoft.AspNetCore.Components.Profiling.WebAssemblyComponentsProfiling', 'SetCapturing');
setCapturingMethod(isCapturing);
});
// dotnet.js assumes the existence of this
window['Browser'] = {
@ -363,11 +358,10 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
resourceLoader.purgeUnusedCacheEntriesAsync(); // Don't await - it's fine to run in background
MONO.mono_wasm_setenv("MONO_URI_DOTNETRELATIVEORABSOLUTE", "true");
MONO.mono_wasm_setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1");
// Turn off full-gc to prevent browser freezing.
const mono_wasm_enable_on_demand_gc = cwrap('mono_wasm_enable_on_demand_gc', null, ['number']);
mono_wasm_enable_on_demand_gc(0);
const load_runtime = cwrap('mono_wasm_load_runtime', null, ['string', 'number']);
// -1 enables debugging with logging disabled. 0 disables debugging entirely.
load_runtime(appBinDirName, hasDebuggingEnabled() ? -1 : 0);

View File

@ -1,134 +0,0 @@
import { System_String } from './Platform';
interface TimingEntry {
// To minimize overhead, don't even decode the strings that arrive from .NET. Assume they are compile-time constants
// and hence the memory address will be fixed, so we can just store the pointer value.
name: string | System_String;
type: 'start' | 'end';
timestamp: number;
}
interface TraceEvent {
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
name: string;
cat: string; // Category
ph: 'B' | 'E'; // Phase
ts: number; // Timestamp in microseconds
pid: number; // Process ID
tid: number; // Thread ID
}
let updateCapturingStateInHost: (isCapturing: boolean) => void;
let captureStartTime = 0;
const blazorProfilingEnabledKey = 'blazorProfilingEnabled';
const profilingEnabled = !!localStorage[blazorProfilingEnabledKey];
const entryLog: TimingEntry[] = [];
const openRegionsStack: (string | System_String)[] = [];
export function setProfilingEnabled(enabled: boolean) {
// We only wire up the hotkeys etc. if the following localStorage entry is present during startup
// This is to ensure we're not interfering with any other hotkeys that developers might want to
// use for different purposes, plus it gives us a single point where we can notify .NET code during
// startup about whether profiling should be enabled.
localStorage[blazorProfilingEnabledKey] = (enabled !== false);
location.reload();
}
export function initializeProfiling(setCapturingCallback: ((isCapturing: boolean) => void) | null) {
if (!profilingEnabled) {
return;
}
updateCapturingStateInHost = setCapturingCallback || (() => {});
// Attach hotkey (alt/cmd)+shift+p
const altKeyName = navigator.platform.match(/^Mac/i) ? 'Cmd' : 'Alt';
console.info(`Profiling hotkey: Shift+${altKeyName}+P (when application has focus)`);
document.addEventListener('keydown', evt => {
if (evt.shiftKey && (evt.metaKey || evt.altKey) && evt.code === 'KeyP') {
toggleCaptureEnabled();
}
});
}
export function profileStart(name: System_String | string) {
if (!captureStartTime) {
return;
}
const startTime = performance.now();
openRegionsStack.push(name);
entryLog.push({ name: name, type: 'start', timestamp: startTime });
}
export function profileEnd(name: System_String | string) {
if (!captureStartTime) {
return;
}
const endTime = performance.now();
const poppedRegionName = openRegionsStack.pop();
if (!poppedRegionName) {
throw new Error(`Profiling mismatch: tried to end profiling for '${readJsString(name)}', but the stack was empty.`);
} else if (poppedRegionName !== name) {
throw new Error(`Profiling mismatch: tried to end profiling for '${readJsString(name)}', but the top stack item was '${readJsString(poppedRegionName)}'.`);
}
entryLog.push({ name: name, type: 'end', timestamp: endTime });
}
function profileReset() {
openRegionsStack.length = 0;
entryLog.length = 0;
captureStartTime = 0;
updateCapturingStateInHost(false);
}
function profileExport() {
const traceEvents: TraceEvent[] = entryLog.map(entry => ({
name: readJsString(entry.name)!,
cat: 'PERF',
ph: entry.type === 'start' ? 'B': 'E',
ts: (entry.timestamp - captureStartTime) * 1000,
pid: 0,
tid: 0,
}));
const traceEventsJson = JSON.stringify(traceEvents);
const traceEventsBuffer = new TextEncoder().encode(traceEventsJson);
const anchorElement = document.createElement('a');
anchorElement.href = URL.createObjectURL(new Blob([traceEventsBuffer]));
anchorElement.setAttribute('download', 'trace.json');
anchorElement.click();
URL.revokeObjectURL(anchorElement.href);
}
function toggleCaptureEnabled() {
if (!captureStartTime) {
displayOverlayMessage('Started capturing performance profile...');
updateCapturingStateInHost(true);
captureStartTime = performance.now();
} else {
displayOverlayMessage('Finished capturing performance profile');
profileExport();
profileReset();
}
}
function displayOverlayMessage(message: string) {
const elem = document.createElement('div');
elem.textContent = message;
elem.setAttribute('style', 'position: absolute; z-index: 99999; font-family: \'Sans Serif\'; top: 0; left: 0; padding: 4px; font-size: 12px; background-color: purple; color: white;');
document.body.appendChild(elem);
setTimeout(() => document.body.removeChild(elem), 3000);
}
function readJsString(str: string | System_String) {
// This is expensive, so don't do it while capturing timings. Only do it as part of the export process.
return typeof str === 'string' ? str : BINDING.conv_string(str);
}
// These globals deliberately differ from our normal conventions for attaching functions inside Blazor.*
// because the intention is to minimize overhead in all reasonable ways. Having any dot-separators in the
// name would cause extra string allocations on every invocation.
window['_blazorProfileStart'] = profileStart;
window['_blazorProfileEnd'] = profileEnd;

View File

@ -165,6 +165,12 @@ async function getCacheToUseIfEnabled(bootConfig: BootJsonData): Promise<Cache |
return null;
}
// cache integrity is compromised if the first request has been served over http
// in this case, we want to disable caching and integrity validation
if (document.location.protocol !== 'https:') {
return null;
}
// Define a separate cache for each base href, so we're isolated from any other
// Blazor application running on the same origin. We need this so that we're free
// to purge from the cache anything we're not using and don't let it keep growing,

View File

@ -9,6 +9,11 @@ export interface WebAssemblyStartOptions {
* @returns A URI string or a Response promise to override the loading process, or null/undefined to allow the default loading behavior.
*/
loadBootResource(type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) : string | Promise<Response> | null | undefined;
/**
* Override built-in environment setting on start.
*/
environment?: string;
}
// This type doesn't have to align with anything in BootConfig.

View File

@ -6,7 +6,6 @@ import { applyCaptureIdToElement } from './ElementReferenceCapture';
import { EventFieldInfo } from './EventFieldInfo';
import { dispatchEvent } from './RendererEventDispatcher';
import { attachToEventDelegator as attachNavigationManagerToEventDelegator } from '../Services/NavigationManager';
import { profileEnd, profileStart } from '../Platform/Profiling';
const selectValuePropname = '_blazorSelectValue';
const sharedTemplateElemForParsing = document.createElement('template');
const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g');
@ -41,8 +40,6 @@ export class BrowserRenderer {
}
public updateComponent(batch: RenderBatch, componentId: number, edits: ArrayBuilderSegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>): void {
profileStart('updateComponent');
const element = this.childComponentLocations[componentId];
if (!element) {
throw new Error(`No element is currently associated with component ${componentId}`);
@ -70,8 +67,6 @@ export class BrowserRenderer {
if ((activeElementBefore instanceof HTMLElement) && ownerDocument && ownerDocument.activeElement !== activeElementBefore) {
activeElementBefore.focus();
}
profileEnd('updateComponent');
}
public disposeComponent(componentId: number) {
@ -238,7 +233,8 @@ export class BrowserRenderer {
document.createElementNS('http://www.w3.org/2000/svg', tagName) :
document.createElement(tagName);
const newElement = toLogicalElement(newDomElementRaw);
insertLogicalChild(newDomElementRaw, parent, childIndex);
let inserted = false;
// Apply attributes
const descendantsEndIndexExcl = frameIndex + frameReader.subtreeLength(frame);
@ -247,6 +243,8 @@ export class BrowserRenderer {
if (frameReader.frameType(descendantFrame) === FrameType.attribute) {
this.applyAttribute(batch, componentId, newDomElementRaw, descendantFrame);
} else {
insertLogicalChild(newDomElementRaw, parent, childIndex);
inserted = true;
// As soon as we see a non-attribute child, all the subsequent child frames are
// not attributes, so bail out and insert the remnants recursively
this.insertFrameRange(batch, componentId, newElement, 0, frames, descendantIndex, descendantsEndIndexExcl);
@ -254,17 +252,40 @@ export class BrowserRenderer {
}
}
// We handle setting 'value' on a <select> in two different ways:
// [1] When inserting a corresponding <option>, in case you're dynamically adding options
// this element did not have any children, so it's not inserted yet.
if (!inserted) {
insertLogicalChild(newDomElementRaw, parent, childIndex);
}
// We handle setting 'value' on a <select> in three different ways:
// [1] When inserting a corresponding <option>, in case you're dynamically adding options.
// This is the case below.
// [2] After we finish inserting the <select>, in case the descendant options are being
// added as an opaque markup block rather than individually
// Right here we implement [2]
if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
// added as an opaque markup block rather than individually. This is the other case below.
// [3] In case the the value of the select and the option value is changed in the same batch.
// We just receive an attribute frame and have to set the select value afterwards.
if (newDomElementRaw instanceof HTMLOptionElement)
{
// Situation 1
this.trySetSelectValueFromOptionElement(newDomElementRaw);
} else if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
// Situation 2
const selectValue: string | null = newDomElementRaw[selectValuePropname];
setSelectElementValue(newDomElementRaw, selectValue);
}
}
private trySetSelectValueFromOptionElement(optionElement: HTMLOptionElement) {
const selectElem = this.findClosestAncestorSelectElement(optionElement);
if (selectElem && (selectValuePropname in selectElem) && selectElem[selectValuePropname] === optionElement.value) {
setSelectElementValue(selectElem, optionElement.value);
delete selectElem[selectValuePropname];
return true;
}
return false;
}
private insertComponent(batch: RenderBatch, parent: LogicalElement, childIndex: number, frame: RenderTreeFrame) {
const containerElement = createAndInsertLogicalContainer(parent, childIndex);
@ -385,13 +406,10 @@ export class BrowserRenderer {
} else {
element.removeAttribute('value');
}
// See above for why we have this special handling for <select>/<option>
// Note that this is only one of the two cases where we set the value on a <select>
const selectElem = this.findClosestAncestorSelectElement(element);
if (selectElem && (selectValuePropname in selectElem) && selectElem[selectValuePropname] === value) {
this.tryApplyValueProperty(batch, selectElem, attributeFrame);
delete selectElem[selectValuePropname];
}
// Situation 3
this.trySetSelectValueFromOptionElement(<HTMLOptionElement>element);
return true;
}
default:

View File

@ -17,6 +17,7 @@ const nonBubblingEvents = toLookup([
'scroll',
'submit',
'unload',
'toggle',
'DOMNodeInsertedIntoDocument',
'DOMNodeRemovedFromDocument',
]);

View File

@ -89,6 +89,9 @@ export class EventForDotNet<TData extends UIEventArgs> {
case 'mousewheel':
return new EventForDotNet<UIWheelEventArgs>('wheel', parseWheelEvent(event as WheelEvent));
case 'toggle':
return new EventForDotNet<UIEventArgs>('toggle', { type: event.type });
default:
return new EventForDotNet<UIEventArgs>('unknown', { type: event.type });
}
@ -248,7 +251,7 @@ function normalizeTimeBasedValue(element: HTMLInputElement): string {
// The following interfaces must be kept in sync with the UIEventArgs C# classes
export type EventArgsType = 'change' | 'clipboard' | 'drag' | 'error' | 'focus' | 'keyboard' | 'mouse' | 'pointer' | 'progress' | 'touch' | 'unknown' | 'wheel';
export type EventArgsType = 'change' | 'clipboard' | 'drag' | 'error' | 'focus' | 'keyboard' | 'mouse' | 'pointer' | 'progress' | 'touch' | 'unknown' | 'wheel' | 'toggle';
export interface UIEventArgs {
type: string;

View File

@ -0,0 +1,86 @@
export const Virtualize = {
init,
dispose,
};
const observersByDotNetId = {};
function findClosestScrollContainer(element: Element | null): Element | null {
if (!element) {
return null;
}
const style = getComputedStyle(element);
if (style.overflowY !== 'visible') {
return element;
}
return findClosestScrollContainer(element.parentElement);
}
function init(dotNetHelper: any, spacerBefore: HTMLElement, spacerAfter: HTMLElement, rootMargin = 50): void {
const intersectionObserver = new IntersectionObserver(intersectionCallback, {
root: findClosestScrollContainer(spacerBefore),
rootMargin: `${rootMargin}px`,
});
intersectionObserver.observe(spacerBefore);
intersectionObserver.observe(spacerAfter);
const mutationObserverBefore = createSpacerMutationObserver(spacerBefore);
const mutationObserverAfter = createSpacerMutationObserver(spacerAfter);
observersByDotNetId[dotNetHelper._id] = {
intersectionObserver,
mutationObserverBefore,
mutationObserverAfter,
};
function createSpacerMutationObserver(spacer: HTMLElement): MutationObserver {
// Without the use of thresholds, IntersectionObserver only detects binary changes in visibility,
// so if a spacer gets resized but remains visible, no additional callbacks will occur. By unobserving
// and reobserving spacers when they get resized, the intersection callback will re-run if they remain visible.
const mutationObserver = new MutationObserver((): void => {
intersectionObserver.unobserve(spacer);
intersectionObserver.observe(spacer);
});
mutationObserver.observe(spacer, { attributes: true });
return mutationObserver;
}
function intersectionCallback(entries: IntersectionObserverEntry[]): void {
entries.forEach((entry): void => {
if (!entry.isIntersecting) {
return;
}
const containerSize = entry.rootBounds?.height;
if (entry.target === spacerBefore) {
dotNetHelper.invokeMethodAsync('OnSpacerBeforeVisible', entry.intersectionRect.top - entry.boundingClientRect.top, containerSize);
} else if (entry.target === spacerAfter && spacerAfter.offsetHeight > 0) {
// When we first start up, both the "before" and "after" spacers will be visible, but it's only relevant to raise a
// single event to load the initial data. To avoid raising two events, skip the one for the "after" spacer if we know
// it's meaningless to talk about any overlap into it.
dotNetHelper.invokeMethodAsync('OnSpacerAfterVisible', entry.boundingClientRect.bottom - entry.intersectionRect.bottom, containerSize);
}
});
}
}
function dispose(dotNetHelper: any): void {
const observers = observersByDotNetId[dotNetHelper._id];
if (observers) {
observers.intersectionObserver.disconnect();
observers.mutationObserverBefore.disconnect();
observers.mutationObserverAfter.disconnect();
dotNetHelper.dispose();
delete observersByDotNetId[dotNetHelper._id];
}
}

View File

@ -14,6 +14,7 @@ describe('DefaultReconnectDisplay', () => {
expect(element).toBeDefined();
expect(element!.id).toBe('test-dialog-id');
expect(element!.style.display).toBe('block');
expect(element!.classList).toContain('show');
expect(display.message.textContent).toBe('Attempting to reconnect to the server...');
expect(display.button.style.display).toBe('none');
@ -36,6 +37,7 @@ describe('DefaultReconnectDisplay', () => {
display.hide();
expect(display.modal.style.display).toBe('none');
expect(display.modal.classList).not.toContain('show');
});
it ('updates message on fail', () => {
@ -46,6 +48,7 @@ describe('DefaultReconnectDisplay', () => {
display.failed();
expect(display.modal.style.display).toBe('block');
expect(display.modal.classList).toContain('show');
expect(display.message.innerHTML).toBe('Reconnection failed. Try <a href=\"\">reloading</a> the page if you\'re unable to reconnect.');
expect(display.button.style.display).toBe('block');
});
@ -58,6 +61,7 @@ describe('DefaultReconnectDisplay', () => {
display.rejected();
expect(display.modal.style.display).toBe('block');
expect(display.modal.classList).toContain('show');
expect(display.message.innerHTML).toBe('Could not reconnect to the server. <a href=\"\">Reload</a> the page to restore functionality.');
expect(display.button.style.display).toBe('none');
});

View File

@ -16,7 +16,8 @@ namespace Microsoft.AspNetCore.Components.Forms
{
private readonly Func<Task> _handleSubmitDelegate; // Cache to avoid per-render allocations
private EditContext? _fixedEditContext;
private EditContext? _editContext;
private bool _hasSetEditContextExplicitly;
/// <summary>
/// Constructs an instance of <see cref="EditForm"/>.
@ -36,7 +37,16 @@ namespace Microsoft.AspNetCore.Components.Forms
/// also supply <see cref="Model"/>, since the model value will be taken
/// from the <see cref="EditContext.Model"/> property.
/// </summary>
[Parameter] public EditContext? EditContext { get; set; }
[Parameter]
public EditContext? EditContext
{
get => _editContext;
set
{
_editContext = value;
_hasSetEditContextExplicitly = value != null;
}
}
/// <summary>
/// Specifies the top-level model object for the form. An edit context will
@ -73,11 +83,16 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <inheritdoc />
protected override void OnParametersSet()
{
if ((EditContext == null) == (Model == null))
if (_hasSetEditContextExplicitly && Model != null)
{
throw new InvalidOperationException($"{nameof(EditForm)} requires a {nameof(Model)} " +
$"parameter, or an {nameof(EditContext)} parameter, but not both.");
}
else if (!_hasSetEditContextExplicitly && Model == null)
{
throw new InvalidOperationException($"{nameof(EditForm)} requires either a {nameof(Model)} " +
$"parameter, or an {nameof(EditContext)} parameter, please provide one of these.");
}
// If you're using OnSubmit, it becomes your responsibility to trigger validation manually
// (e.g., so you can display a "pending" state in the UI). In that case you don't want the
@ -89,31 +104,31 @@ namespace Microsoft.AspNetCore.Components.Forms
$"{nameof(EditForm)}, do not also supply {nameof(OnValidSubmit)} or {nameof(OnInvalidSubmit)}.");
}
// Update _fixedEditContext if we don't have one yet, or if they are supplying a
// Update _editContext if we don't have one yet, or if they are supplying a
// potentially new EditContext, or if they are supplying a different Model
if (_fixedEditContext == null || EditContext != null || Model != _fixedEditContext.Model)
if (Model != null && Model != _editContext?.Model)
{
_fixedEditContext = EditContext ?? new EditContext(Model!);
_editContext = new EditContext(Model!);
}
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
Debug.Assert(_fixedEditContext != null);
Debug.Assert(_editContext != null);
// If _fixedEditContext changes, tear down and recreate all descendants.
// If _editContext changes, tear down and recreate all descendants.
// This is so we can safely use the IsFixed optimization on CascadingValue,
// optimizing for the common case where _fixedEditContext never changes.
builder.OpenRegion(_fixedEditContext.GetHashCode());
// optimizing for the common case where _editContext never changes.
builder.OpenRegion(_editContext.GetHashCode());
builder.OpenElement(0, "form");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "onsubmit", _handleSubmitDelegate);
builder.OpenComponent<CascadingValue<EditContext>>(3);
builder.AddAttribute(4, "IsFixed", true);
builder.AddAttribute(5, "Value", _fixedEditContext);
builder.AddAttribute(6, "ChildContent", ChildContent?.Invoke(_fixedEditContext));
builder.AddAttribute(5, "Value", _editContext);
builder.AddAttribute(6, "ChildContent", ChildContent?.Invoke(_editContext));
builder.CloseComponent();
builder.CloseElement();
@ -122,26 +137,26 @@ namespace Microsoft.AspNetCore.Components.Forms
private async Task HandleSubmitAsync()
{
Debug.Assert(_fixedEditContext != null);
Debug.Assert(_editContext != null);
if (OnSubmit.HasDelegate)
{
// When using OnSubmit, the developer takes control of the validation lifecycle
await OnSubmit.InvokeAsync(_fixedEditContext);
await OnSubmit.InvokeAsync(_editContext);
}
else
{
// Otherwise, the system implicitly runs validation on form submission
var isValid = _fixedEditContext.Validate(); // This will likely become ValidateAsync later
var isValid = _editContext.Validate(); // This will likely become ValidateAsync later
if (isValid && OnValidSubmit.HasDelegate)
{
await OnValidSubmit.InvokeAsync(_fixedEditContext);
await OnValidSubmit.InvokeAsync(_editContext);
}
if (!isValid && OnInvalidSubmit.HasDelegate)
{
await OnInvalidSubmit.InvokeAsync(_fixedEditContext);
await OnInvalidSubmit.InvokeAsync(_editContext);
}
}
}

View File

@ -50,6 +50,12 @@ namespace Microsoft.AspNetCore.Components.Forms
/// </summary>
[Parameter] public Expression<Func<TValue>>? ValueExpression { get; set; }
/// <summary>
/// Gets or sets the display name for this field.
/// <para>This value is used when generating error messages when the input value fails to parse correctly.</para>
/// </summary>
[Parameter] public string? DisplayName { get; set; }
/// <summary>
/// Gets the associated <see cref="Forms.EditContext"/>.
/// </summary>

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Components.Forms
}
else
{
validationErrorMessage = string.Format(ParsingErrorMessage, FieldIdentifier.FieldName);
validationErrorMessage = string.Format(ParsingErrorMessage, DisplayName ?? FieldIdentifier.FieldName);
return false;
}
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.Forms
else
{
result = default;
validationErrorMessage = $"The {input.FieldIdentifier.FieldName} field is not valid.";
validationErrorMessage = $"The {input.DisplayName ?? input.FieldIdentifier.FieldName} field is not valid.";
return false;
}
}

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Components.Forms
}
else
{
validationErrorMessage = string.Format(ParsingErrorMessage, FieldIdentifier.FieldName);
validationErrorMessage = string.Format(ParsingErrorMessage, DisplayName ?? FieldIdentifier.FieldName);
return false;
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
internal interface IVirtualizeJsCallbacks
{
void OnBeforeSpacerVisible(float spacerSize, float containerSize);
void OnAfterSpacerVisible(float spacerSize, float containerSize);
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
/// <summary>
/// A function that provides items to a virtualized source.
/// </summary>
/// <typeparam name="TItem">The type of the context for each item in the list.</typeparam>
/// <param name="request">The <see cref="ItemsProviderRequest"/> defining the request details.</param>
/// <returns>A <see cref="ValueTask"/> whose result is a <see cref="ItemsProviderResult{TItem}"/> upon successful completion.</returns>
public delegate ValueTask<ItemsProviderResult<TItem>> ItemsProviderDelegate<TItem>(ItemsProviderRequest request);
}

View File

@ -0,0 +1,44 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
/// <summary>
/// Represents a request to an <see cref="ItemsProviderDelegate{TItem}"/>.
/// </summary>
public readonly struct ItemsProviderRequest
{
/// <summary>
/// The start index of the data segment requested.
/// </summary>
public int StartIndex { get; }
/// <summary>
/// The requested number of items to be provided. The actual number of provided items does not need to match
/// this value.
/// </summary>
public int Count { get; }
/// <summary>
/// The <see cref="System.Threading.CancellationToken"/> used to relay cancellation of the request.
/// </summary>
public CancellationToken CancellationToken { get; }
/// <summary>
/// Constructs a new <see cref="ItemsProviderRequest"/> instance.
/// </summary>
/// <param name="startIndex">The start index of the data segment requested.</param>
/// <param name="count">The requested number of items to be provided.</param>
/// <param name="cancellationToken">
/// The <see cref="System.Threading.CancellationToken"/> used to relay cancellation of the request.
/// </param>
public ItemsProviderRequest(int startIndex, int count, CancellationToken cancellationToken)
{
StartIndex = startIndex;
Count = count;
CancellationToken = cancellationToken;
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
/// <summary>
/// Represents the result of a <see cref="ItemsProviderDelegate{TItem}"/>.
/// </summary>
/// <typeparam name="TItem">The type of the context for each item in the list.</typeparam>
public readonly struct ItemsProviderResult<TItem>
{
/// <summary>
/// The items to provide.
/// </summary>
public IEnumerable<TItem> Items { get; }
/// <summary>
/// The total item count in the source generating the items provided.
/// </summary>
public int TotalItemCount { get; }
/// <summary>
/// Instantiates a new <see cref="ItemsProviderResult{TItem}"/> instance.
/// </summary>
/// <param name="items">The items to provide.</param>
/// <param name="totalItemCount">The total item count in the source generating the items provided.</param>
public ItemsProviderResult(IEnumerable<TItem> items, int totalItemCount)
{
Items = items;
TotalItemCount = totalItemCount;
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
/// <summary>
/// Contains context for a placeholder in a virtualized list.
/// </summary>
public readonly struct PlaceholderContext
{
/// <summary>
/// The item index of the placeholder.
/// </summary>
public int Index { get; }
/// <summary>
/// Constructs a new <see cref="PlaceholderContext"/> instance.
/// </summary>
/// <param name="index">The item index of the placeholder.</param>
public PlaceholderContext(int index)
{
Index = index;
}
}
}

View File

@ -0,0 +1,303 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
/// <summary>
/// Provides functionality for rendering a virtualized list of items.
/// </summary>
/// <typeparam name="TItem">The <c>context</c> type for the items being rendered.</typeparam>
public sealed class Virtualize<TItem> : ComponentBase, IVirtualizeJsCallbacks, IAsyncDisposable
{
private VirtualizeJsInterop? _jsInterop;
private ElementReference _spacerBefore;
private ElementReference _spacerAfter;
private int _itemsBefore;
private int _visibleItemCapacity;
private int _itemCount;
private int _loadedItemsStartIndex;
private IEnumerable<TItem>? _loadedItems;
private CancellationTokenSource? _refreshCts;
private Exception? _refreshException;
private ItemsProviderDelegate<TItem> _itemsProvider = default!;
private RenderFragment<TItem>? _itemTemplate;
private RenderFragment<PlaceholderContext>? _placeholder;
[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;
/// <summary>
/// Gets or sets the item template for the list.
/// </summary>
[Parameter]
public RenderFragment<TItem>? ChildContent { get; set; }
/// <summary>
/// Gets or sets the item template for the list.
/// </summary>
[Parameter]
public RenderFragment<TItem>? ItemContent { get; set; }
/// <summary>
/// Gets or sets the template for items that have not yet been loaded in memory.
/// </summary>
[Parameter]
public RenderFragment<PlaceholderContext>? Placeholder { get; set; }
/// <summary>
/// Gets the size of each item in pixels.
/// </summary>
[Parameter]
public float ItemSize { get; set; }
/// <summary>
/// Gets or sets the function providing items to the list.
/// </summary>
[Parameter]
public ItemsProviderDelegate<TItem>? ItemsProvider { get; set; }
/// <summary>
/// Gets or sets the fixed item source.
/// </summary>
[Parameter]
public ICollection<TItem>? Items { get; set; }
/// <inheritdoc />
protected override void OnParametersSet()
{
if (ItemSize <= 0)
{
throw new InvalidOperationException(
$"{GetType()} requires a positive value for parameter '{nameof(ItemSize)}' to perform virtualization.");
}
if (ItemsProvider != null)
{
if (Items != null)
{
throw new InvalidOperationException(
$"{GetType()} can only accept one item source from its parameters. " +
$"Do not supply both '{nameof(Items)}' and '{nameof(ItemsProvider)}'.");
}
_itemsProvider = ItemsProvider;
}
else if (Items != null)
{
_itemsProvider = DefaultItemsProvider;
}
else
{
throw new InvalidOperationException(
$"{GetType()} requires either the '{nameof(Items)}' or '{nameof(ItemsProvider)}' parameters to be specified " +
$"and non-null.");
}
_itemTemplate = ItemContent ?? ChildContent;
_placeholder = Placeholder ?? DefaultPlaceholder;
}
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsInterop = new VirtualizeJsInterop(this, JSRuntime);
await _jsInterop.InitializeAsync(_spacerBefore, _spacerAfter);
}
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (_refreshException != null)
{
var oldRefreshException = _refreshException;
_refreshException = null;
throw oldRefreshException;
}
builder.OpenElement(0, "div");
builder.AddAttribute(1, "style", GetSpacerStyle(_itemsBefore));
builder.AddElementReferenceCapture(2, elementReference => _spacerBefore = elementReference);
builder.CloseElement();
var lastItemIndex = Math.Min(_itemsBefore + _visibleItemCapacity, _itemCount);
var renderIndex = _itemsBefore;
var placeholdersBeforeCount = Math.Min(_loadedItemsStartIndex, lastItemIndex);
builder.OpenRegion(3);
// Render placeholders before the loaded items.
for (; renderIndex < placeholdersBeforeCount; renderIndex++)
{
// This is a rare case where it's valid for the sequence number to be programmatically incremented.
// This is only true because we know for certain that no other content will be alongside it.
builder.AddContent(renderIndex, _placeholder, new PlaceholderContext(renderIndex));
}
builder.CloseRegion();
// Render the loaded items.
if (_loadedItems != null && _itemTemplate != null)
{
var itemsToShow = _loadedItems
.Skip(_itemsBefore - _loadedItemsStartIndex)
.Take(lastItemIndex - _loadedItemsStartIndex);
builder.OpenRegion(4);
foreach (var item in itemsToShow)
{
_itemTemplate(item)(builder);
renderIndex++;
}
builder.CloseRegion();
}
builder.OpenRegion(5);
// Render the placeholders after the loaded items.
for (; renderIndex < lastItemIndex; renderIndex++)
{
builder.AddContent(renderIndex, _placeholder, new PlaceholderContext(renderIndex));
}
builder.CloseRegion();
var itemsAfter = Math.Max(0, _itemCount - _visibleItemCapacity - _itemsBefore);
builder.OpenElement(6, "div");
builder.AddAttribute(7, "style", GetSpacerStyle(itemsAfter));
builder.AddElementReferenceCapture(8, elementReference => _spacerAfter = elementReference);
builder.CloseElement();
}
private string GetSpacerStyle(int itemsInSpacer)
=> $"height: {itemsInSpacer * ItemSize}px;";
void IVirtualizeJsCallbacks.OnBeforeSpacerVisible(float spacerSize, float containerSize)
{
CalcualteItemDistribution(spacerSize, containerSize, out var itemsBefore, out var visibleItemCapacity);
UpdateItemDistribution(itemsBefore, visibleItemCapacity);
}
void IVirtualizeJsCallbacks.OnAfterSpacerVisible(float spacerSize, float containerSize)
{
CalcualteItemDistribution(spacerSize, containerSize, out var itemsAfter, out var visibleItemCapacity);
var itemsBefore = Math.Max(0, _itemCount - itemsAfter - visibleItemCapacity);
UpdateItemDistribution(itemsBefore, visibleItemCapacity);
}
private void CalcualteItemDistribution(float spacerSize, float containerSize, out int itemsInSpacer, out int visibleItemCapacity)
{
itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / ItemSize) - 1);
visibleItemCapacity = (int)Math.Ceiling(containerSize / ItemSize) + 2;
}
private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity)
{
if (itemsBefore != _itemsBefore || visibleItemCapacity != _visibleItemCapacity)
{
_itemsBefore = itemsBefore;
_visibleItemCapacity = visibleItemCapacity;
var refreshTask = RefreshDataAsync();
if (!refreshTask.IsCompleted)
{
StateHasChanged();
}
}
}
private async Task RefreshDataAsync()
{
_refreshCts?.Cancel();
_refreshCts = new CancellationTokenSource();
var cancellationToken = _refreshCts.Token;
var request = new ItemsProviderRequest(_itemsBefore, _visibleItemCapacity, cancellationToken);
try
{
var result = await _itemsProvider(request);
// Only apply result if the task was not canceled.
if (!cancellationToken.IsCancellationRequested)
{
_itemCount = result.TotalItemCount;
_loadedItems = result.Items;
_loadedItemsStartIndex = request.StartIndex;
StateHasChanged();
}
}
catch (Exception e)
{
if (e is OperationCanceledException oce && oce.CancellationToken == cancellationToken)
{
// No-op; we canceled the operation, so it's fine to suppress this exception.
}
else
{
// Cache this exception so the renderer can throw it.
_refreshException = e;
// Re-render the component to throw the exception.
StateHasChanged();
}
}
}
private ValueTask<ItemsProviderResult<TItem>> DefaultItemsProvider(ItemsProviderRequest request)
{
return ValueTask.FromResult(new ItemsProviderResult<TItem>(
Items!.Skip(request.StartIndex).Take(request.Count),
Items!.Count));
}
private RenderFragment DefaultPlaceholder(PlaceholderContext context) => (builder) =>
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "style", $"height: {ItemSize}px;");
builder.CloseElement();
};
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
_refreshCts?.Cancel();
if (_jsInterop != null)
{
await _jsInterop.DisposeAsync();
}
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web.Virtualization
{
internal class VirtualizeJsInterop : IAsyncDisposable
{
private const string JsFunctionsPrefix = "Blazor._internal.Virtualize";
private readonly IVirtualizeJsCallbacks _owner;
private readonly IJSRuntime _jsRuntime;
private DotNetObjectReference<VirtualizeJsInterop>? _selfReference;
public VirtualizeJsInterop(IVirtualizeJsCallbacks owner, IJSRuntime jsRuntime)
{
_owner = owner;
_jsRuntime = jsRuntime;
}
public async ValueTask InitializeAsync(ElementReference spacerBefore, ElementReference spacerAfter)
{
_selfReference = DotNetObjectReference.Create(this);
await _jsRuntime.InvokeVoidAsync($"{JsFunctionsPrefix}.init", _selfReference, spacerBefore, spacerAfter);
}
[JSInvokable]
public void OnSpacerBeforeVisible(float spacerSize, float containerSize)
{
_owner.OnBeforeSpacerVisible(spacerSize, containerSize);
}
[JSInvokable]
public void OnSpacerAfterVisible(float spacerSize, float containerSize)
{
_owner.OnAfterSpacerVisible(spacerSize, containerSize);
}
public async ValueTask DisposeAsync()
{
if (_selfReference != null)
{
await _jsRuntime.InvokeVoidAsync($"{JsFunctionsPrefix}.dispose", _selfReference);
}
}
}
}

View File

@ -122,6 +122,8 @@ namespace Microsoft.AspNetCore.Components.Web
[EventHandler("onpointerlockerror", typeof(EventArgs), true, true)]
[EventHandler("onreadystatechange", typeof(EventArgs), true, true)]
[EventHandler("onscroll", typeof(EventArgs), true, true)]
[EventHandler("ontoggle", typeof(EventArgs), true, true)]
public static class EventHandlers
{
}

View File

@ -0,0 +1,120 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
namespace Microsoft.AspNetCore.Components.Forms
{
public class EditFormTest
{
[Fact]
public async Task ThrowsIfBothEditContextAndModelAreSupplied()
{
// Arrange
var editForm = new EditForm
{
EditContext = new EditContext(new TestModel()),
Model = new TestModel()
};
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(editForm);
// Act/Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => testRenderer.RenderRootComponentAsync(componentId));
Assert.StartsWith($"{nameof(EditForm)} requires a {nameof(EditForm.Model)} parameter, or an {nameof(EditContext)} parameter, but not both.", ex.Message);
}
[Fact]
public async Task ThrowsIfBothEditContextAndModelAreNull()
{
// Arrange
var editForm = new EditForm();
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(editForm);
// Act/Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => testRenderer.RenderRootComponentAsync(componentId));
Assert.StartsWith($"{nameof(EditForm)} requires either a {nameof(EditForm.Model)} parameter, or an {nameof(EditContext)} parameter, please provide one of these.", ex.Message);
}
[Fact]
public async Task ReturnsEditContextWhenModelParameterUsed()
{
// Arrange
var model = new TestModel();
var rootComponent = new TestEditFormHostComponent
{
Model = model
};
var editFormComponent = await RenderAndGetTestEditFormComponentAsync(rootComponent);
// Act
var returnedEditContext = editFormComponent.EditContext;
// Assert
Assert.NotNull(returnedEditContext);
Assert.Same(model, returnedEditContext.Model);
}
[Fact]
public async Task ReturnsEditContextWhenEditContextParameterUsed()
{
// Arrange
var editContext = new EditContext(new TestModel());
var rootComponent = new TestEditFormHostComponent
{
EditContext = editContext
};
var editFormComponent = await RenderAndGetTestEditFormComponentAsync(rootComponent);
// Act
var returnedEditContext = editFormComponent.EditContext;
// Assert
Assert.Same(editContext, returnedEditContext);
}
private static EditForm FindEditFormComponent(CapturedBatch batch)
=> batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component)
.OfType<EditForm>()
.Single();
private static async Task<EditForm> RenderAndGetTestEditFormComponentAsync(TestEditFormHostComponent hostComponent)
{
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(hostComponent);
await testRenderer.RenderRootComponentAsync(componentId);
return FindEditFormComponent(testRenderer.Batches.Single());
}
class TestModel
{
public string StringProperty { get; set; }
}
class TestEditFormHostComponent : AutoRenderComponent
{
public EditContext EditContext { get; set; }
public TestModel Model { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenComponent<EditForm>(0);
builder.AddAttribute(1, "Model", Model);
builder.AddAttribute(2, "EditContext", EditContext);
builder.CloseComponent();
}
}
}
}

View File

@ -4,10 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
@ -35,7 +32,7 @@ namespace Microsoft.AspNetCore.Components.Forms
// Arrange
var model = new TestModel();
var rootComponent = new TestInputHostComponent<string, TestInputComponent<string>> { EditContext = new EditContext(model), ValueExpression = () => model.StringProperty };
await RenderAndGetTestInputComponentAsync(rootComponent);
await InputRenderer.RenderAndGetComponent(rootComponent);
// Act/Assert
rootComponent.EditContext = new EditContext(model);
@ -51,7 +48,7 @@ namespace Microsoft.AspNetCore.Components.Forms
var rootComponent = new TestInputHostComponent<string, TestInputComponent<string>> { EditContext = new EditContext(model) };
// Act/Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => RenderAndGetTestInputComponentAsync(rootComponent));
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => InputRenderer.RenderAndGetComponent(rootComponent));
Assert.Contains($"{typeof(TestInputComponent<string>)} requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.", ex.Message);
}
@ -68,7 +65,7 @@ namespace Microsoft.AspNetCore.Components.Forms
};
// Act
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Assert
Assert.Equal("some value", inputComponent.CurrentValue);
@ -87,7 +84,7 @@ namespace Microsoft.AspNetCore.Components.Forms
};
// Act
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Assert
Assert.Same(rootComponent.EditContext, inputComponent.EditContext);
@ -106,7 +103,7 @@ namespace Microsoft.AspNetCore.Components.Forms
};
// Act
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Assert
Assert.Equal(FieldIdentifier.Create(() => model.StringProperty), inputComponent.FieldIdentifier);
@ -123,7 +120,7 @@ namespace Microsoft.AspNetCore.Components.Forms
Value = "initial value",
ValueExpression = () => model.StringProperty
};
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.Equal("initial value", inputComponent.CurrentValue);
// Act
@ -146,7 +143,7 @@ namespace Microsoft.AspNetCore.Components.Forms
ValueChanged = val => valueChangedCallLog.Add(val),
ValueExpression = () => model.StringProperty
};
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.Empty(valueChangedCallLog);
// Act
@ -169,7 +166,7 @@ namespace Microsoft.AspNetCore.Components.Forms
ValueChanged = val => valueChangedCallLog.Add(val),
ValueExpression = () => model.StringProperty
};
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.Empty(valueChangedCallLog);
// Act
@ -190,7 +187,7 @@ namespace Microsoft.AspNetCore.Components.Forms
Value = "initial value",
ValueExpression = () => model.StringProperty
};
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.False(rootComponent.EditContext.IsModified(() => model.StringProperty));
// Act
@ -213,7 +210,7 @@ namespace Microsoft.AspNetCore.Components.Forms
var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty);
// Act/Assert: Initially, it's valid and unmodified
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.Equal("valid", inputComponent.CssClass); // no Class was specified
// Act/Assert: Modify the field
@ -251,7 +248,7 @@ namespace Microsoft.AspNetCore.Components.Forms
var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty);
// Act/Assert
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
Assert.Equal("my-class other-class valid", inputComponent.CssClass);
// Act/Assert: Retains custom class when changing field class
@ -270,7 +267,7 @@ namespace Microsoft.AspNetCore.Components.Forms
Value = new DateTime(1915, 3, 2),
ValueExpression = () => model.DateProperty
};
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act/Assert
Assert.Equal("1915/03/02", inputComponent.CurrentValueAsString);
@ -289,7 +286,7 @@ namespace Microsoft.AspNetCore.Components.Forms
ValueExpression = () => model.DateProperty
};
var fieldIdentifier = FieldIdentifier.Create(() => model.DateProperty);
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
var numValidationStateChanges = 0;
rootComponent.EditContext.OnValidationStateChanged += (sender, eventArgs) => { numValidationStateChanges++; };
@ -319,7 +316,7 @@ namespace Microsoft.AspNetCore.Components.Forms
ValueExpression = () => model.DateProperty
};
var fieldIdentifier = FieldIdentifier.Create(() => model.DateProperty);
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
var numValidationStateChanges = 0;
rootComponent.EditContext.OnValidationStateChanged += (sender, eventArgs) => { numValidationStateChanges++; };
@ -470,21 +467,6 @@ namespace Microsoft.AspNetCore.Components.Forms
Assert.Equal("userSpecifiedValue", component.AdditionalAttributes["aria-invalid"]);
}
private static TComponent FindComponent<TComponent>(CapturedBatch batch)
=> batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component)
.OfType<TComponent>()
.Single();
private static async Task<TComponent> RenderAndGetTestInputComponentAsync<TValue, TComponent>(TestInputHostComponent<TValue, TComponent> hostComponent) where TComponent : TestInputComponent<TValue>
{
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(hostComponent);
await testRenderer.RenderRootComponentAsync(componentId);
return FindComponent<TComponent>(testRenderer.Batches.Single());
}
class TestModel
{
public string StringProperty { get; set; }
@ -530,7 +512,7 @@ namespace Microsoft.AspNetCore.Components.Forms
}
}
class TestDateInputComponent : TestInputComponent<DateTime>
private class TestDateInputComponent : TestInputComponent<DateTime>
{
protected override string FormatValueAsString(DateTime value)
=> value.ToString("yyyy/MM/dd");
@ -549,35 +531,5 @@ namespace Microsoft.AspNetCore.Components.Forms
}
}
}
class TestInputHostComponent<TValue, TComponent> : AutoRenderComponent where TComponent : TestInputComponent<TValue>
{
public Dictionary<string, object> AdditionalAttributes { get; set; }
public EditContext EditContext { get; set; }
public TValue Value { get; set; }
public Action<TValue> ValueChanged { get; set; }
public Expression<Func<TValue>> ValueExpression { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenComponent<CascadingValue<EditContext>>(0);
builder.AddAttribute(1, "Value", EditContext);
builder.AddAttribute(2, "ChildContent", new RenderFragment(childBuilder =>
{
childBuilder.OpenComponent<TComponent>(0);
childBuilder.AddAttribute(0, "Value", Value);
childBuilder.AddAttribute(1, "ValueChanged",
EventCallback.Factory.Create(this, ValueChanged));
childBuilder.AddAttribute(2, "ValueExpression", ValueExpression);
childBuilder.AddMultipleAttributes(3, AdditionalAttributes);
childBuilder.CloseComponent();
}));
builder.CloseComponent();
}
}
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Components.Forms
{
public class InputDateTest
{
[Fact]
public async Task ValidationErrorUsesDisplayAttributeName()
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputHostComponent<DateTime, TestInputDateComponent>
{
EditContext = new EditContext(model),
ValueExpression = () => model.DateProperty,
AdditionalAttributes = new Dictionary<string, object>
{
{ "DisplayName", "Date property" }
}
};
var fieldIdentifier = FieldIdentifier.Create(() => model.DateProperty);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
await inputComponent.SetCurrentValueAsStringAsync("invalidDate");
// Assert
var validationMessages = rootComponent.EditContext.GetValidationMessages(fieldIdentifier);
Assert.NotEmpty(validationMessages);
Assert.Contains("The Date property field must be a date.", validationMessages);
}
private class TestModel
{
public DateTime DateProperty { get; set; }
}
private class TestInputDateComponent : InputDate<DateTime>
{
public async Task SetCurrentValueAsStringAsync(string value)
{
// This is equivalent to the subclass writing to CurrentValueAsString
// (e.g., from @bind), except to simplify the test code there's an InvokeAsync
// here. In production code it wouldn't normally be required because @bind
// calls run on the sync context anyway.
await InvokeAsync(() => { base.CurrentValueAsString = value; });
}
}
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Components.Forms
{
public class InputNumberTest
{
[Fact]
public async Task ValidationErrorUsesDisplayAttributeName()
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputHostComponent<int, TestInputNumberComponent>
{
EditContext = new EditContext(model),
ValueExpression = () => model.SomeNumber,
AdditionalAttributes = new Dictionary<string, object>
{
{ "DisplayName", "Some number" }
}
};
var fieldIdentifier = FieldIdentifier.Create(() => model.SomeNumber);
var inputComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
await inputComponent.SetCurrentValueAsStringAsync("notANumber");
// Assert
var validationMessages = rootComponent.EditContext.GetValidationMessages(fieldIdentifier);
Assert.NotEmpty(validationMessages);
Assert.Contains("The Some number field must be a number.", validationMessages);
}
private class TestModel
{
public int SomeNumber { get; set; }
}
private class TestInputNumberComponent : InputNumber<int>
{
public async Task SetCurrentValueAsStringAsync(string value)
{
// This is equivalent to the subclass writing to CurrentValueAsString
// (e.g., from @bind), except to simplify the test code there's an InvokeAsync
// here. In production code it wouldn't normally be required because @bind
// calls run on the sync context anyway.
await InvokeAsync(() => { base.CurrentValueAsString = value; });
}
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
namespace Microsoft.AspNetCore.Components.Forms
{
internal static class InputRenderer
{
public static async Task<TComponent> RenderAndGetComponent<TValue, TComponent>(TestInputHostComponent<TValue, TComponent> hostComponent)
where TComponent : InputBase<TValue>
{
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(hostComponent);
await testRenderer.RenderRootComponentAsync(componentId);
return FindComponent<TComponent>(testRenderer.Batches.Single());
}
private static TComponent FindComponent<TComponent>(CapturedBatch batch)
=> batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component)
.OfType<TComponent>()
.Single();
}
}

View File

@ -2,12 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
namespace Microsoft.AspNetCore.Components.Forms
@ -19,12 +15,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<TestEnum>
var rootComponent = new TestInputHostComponent<TestEnum, TestInputSelect<TestEnum>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NotNullableEnum
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "Two";
@ -38,12 +34,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<TestEnum>
var rootComponent = new TestInputHostComponent<TestEnum, TestInputSelect<TestEnum>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NotNullableEnum
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "";
@ -57,12 +53,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<TestEnum?>
var rootComponent = new TestInputHostComponent<TestEnum?, TestInputSelect<TestEnum?>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NullableEnum
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "Two";
@ -76,12 +72,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<TestEnum?>
var rootComponent = new TestInputHostComponent<TestEnum?, TestInputSelect<TestEnum?>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NullableEnum
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "";
@ -96,12 +92,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<Guid>
var rootComponent = new TestInputHostComponent<Guid, TestInputSelect<Guid>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NotNullableGuid
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
var guid = Guid.NewGuid();
@ -117,12 +113,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<Guid?>
var rootComponent = new TestInputHostComponent<Guid?, TestInputSelect<Guid?>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NullableGuid
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
var guid = Guid.NewGuid();
@ -138,12 +134,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<int>
var rootComponent = new TestInputHostComponent<int, TestInputSelect<int>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NotNullableInt
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "42";
@ -158,12 +154,12 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Arrange
var model = new TestModel();
var rootComponent = new TestInputSelectHostComponent<int?>
var rootComponent = new TestInputHostComponent<int?, TestInputSelect<int?>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NullableInt
};
var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
inputSelectComponent.CurrentValueAsString = "42";
@ -172,19 +168,30 @@ namespace Microsoft.AspNetCore.Components.Forms
Assert.Equal(42, inputSelectComponent.CurrentValue);
}
private static TestInputSelect<TValue> FindInputSelectComponent<TValue>(CapturedBatch batch)
=> batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component)
.OfType<TestInputSelect<TValue>>()
.Single();
private static async Task<TestInputSelect<TValue>> RenderAndGetTestInputComponentAsync<TValue>(TestInputSelectHostComponent<TValue> hostComponent)
[Fact]
public async Task ValidationErrorUsesDisplayAttributeName()
{
var testRenderer = new TestRenderer();
var componentId = testRenderer.AssignRootComponentId(hostComponent);
await testRenderer.RenderRootComponentAsync(componentId);
return FindInputSelectComponent<TValue>(testRenderer.Batches.Single());
// Arrange
var model = new TestModel();
var rootComponent = new TestInputHostComponent<int, TestInputSelect<int>>
{
EditContext = new EditContext(model),
ValueExpression = () => model.NotNullableInt,
AdditionalAttributes = new Dictionary<string, object>
{
{ "DisplayName", "Some number" }
}
};
var fieldIdentifier = FieldIdentifier.Create(() => model.NotNullableInt);
var inputSelectComponent = await InputRenderer.RenderAndGetComponent(rootComponent);
// Act
await inputSelectComponent.SetCurrentValueAsStringAsync("invalidNumber");
// Assert
var validationMessages = rootComponent.EditContext.GetValidationMessages(fieldIdentifier);
Assert.NotEmpty(validationMessages);
Assert.Contains("The Some number field is not valid.", validationMessages);
}
enum TestEnum
@ -218,25 +225,13 @@ namespace Microsoft.AspNetCore.Components.Forms
get => base.CurrentValueAsString;
set => base.CurrentValueAsString = value;
}
}
class TestInputSelectHostComponent<TValue> : AutoRenderComponent
{
public EditContext EditContext { get; set; }
public Expression<Func<TValue>> ValueExpression { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
public async Task SetCurrentValueAsStringAsync(string value)
{
builder.OpenComponent<CascadingValue<EditContext>>(0);
builder.AddAttribute(1, "Value", EditContext);
builder.AddAttribute(2, "ChildContent", new RenderFragment(childBuilder =>
{
childBuilder.OpenComponent<TestInputSelect<TValue>>(0);
childBuilder.AddAttribute(0, "ValueExpression", ValueExpression);
childBuilder.CloseComponent();
}));
builder.CloseComponent();
// This is equivalent to the subclass writing to CurrentValueAsString
// (e.g., from @bind), except to simplify the test code there's an InvokeAsync
// here. In production code it wouldn't normally be required because @bind
// calls run on the sync context anyway.
await InvokeAsync(() => { base.CurrentValueAsString = value; });
}
}
}

Some files were not shown because too many files have changed in this diff Show More