Merge branch 'merge/release/2.2-to-master' of https://github.com/dotnet-maestro-bot/Mvc into dotnet-maestro-bot-merge/release/2.2-to-master

This commit is contained in:
Ryan Nowak 2018-09-06 16:09:34 -07:00
commit f7d9271d71
161 changed files with 1597 additions and 433 deletions

17
Mvc.sln
View File

@ -178,7 +178,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicViews", "benchmarkapps
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Api.Analyzers.Test", "test\Mvc.Api.Analyzers.Test\Mvc.Api.Analyzers.Test.csproj", "{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Api.Analyzers", "src\Microsoft.AspNetCore.Mvc.Api.Analyzers\Microsoft.AspNetCore.Mvc.Api.Analyzers.csproj", "{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Api.Analyzers", "src\Microsoft.AspNetCore.Mvc.Api.Analyzers\Microsoft.AspNetCore.Mvc.Api.Analyzers.csproj", "{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorRendering", "benchmarkapps\RazorRendering\RazorRendering.csproj", "{D7C6A696-F232-4288-BCCD-367407E4A934}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -952,6 +954,18 @@ Global
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.ActiveCfg = Release|Any CPU
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.Build.0 = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|x86.ActiveCfg = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|x86.Build.0 = Debug|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Any CPU.Build.0 = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.ActiveCfg = Release|Any CPU
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1025,6 +1039,7 @@ Global
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{D7C6A696-F232-4288-BCCD-367407E4A934} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {63D344F6-F86D-40E6-85B9-0AABBE338C4A}

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Html;
namespace Data
{
public class DataA
{
public DataA(int id, HtmlString icon, HtmlString html, string name, int seconds, int max, float perHour)
{
Id = id;
Icon = icon;
Html = html;
Name = name;
Seconds = seconds;
Max = max;
PerHour = perHour;
}
public int Id { get; }
public HtmlString Icon { get; }
public HtmlString Html { get; }
public string Name { get; }
public int Seconds { get; }
public int Max { get; }
public float PerHour { get; }
}
}

View File

@ -0,0 +1,25 @@
using System;
using Microsoft.AspNetCore.Html;
namespace Data
{
public class DataB
{
public DataB(int id, HtmlString icon, string name, int value, DateTimeOffset startDate, DateTimeOffset completeDate)
{
Id = id;
Icon = icon;
Name = name;
Value = value;
StartDate = startDate;
CompleteDate = completeDate;
}
public int Id { get; }
public HtmlString Icon { get; }
public string Name { get; }
public int Value { get; }
public DateTimeOffset StartDate { get; }
public DateTimeOffset CompleteDate { get; }
}
}

View File

@ -0,0 +1,138 @@
@page
@model Pages.PageA
@using static System.Convert
@section Subcategories {
<partial name="_Subcategories" />
}
@section Tabs {
<span class="dialog-tab@(Model.Condition ? " selected" : "")"><span class="link" data-url="/Category/PageA">Sub cat A</span></span>
<span class="dialog-tab"><span class="link" data-url="/Category/PageB/@Model.Value1/@Model.Value2">Sub cat B</span></span>
<span class="dialog-tab"><span class="link" data-url="/Category/PageC/@((int)Model.Value3)">Sub cat C</span></span>
<span class="dialog-tab@(!Model.Condition ? " selected" : "")"><span class="link" data-url="/Category/PageD/@((int)Model.Value3)">Sub cat D</span></span>
}
@switch (Model.Value)
{
case 0:
<h1>@Model.Name Type A</h1>
break;
case 1:
<h1>@Model.Name Type B</h1>
break;
case 2:
<h1>@Model.Name Type C</h1>
break;
}
<h3 style="clear:both;">Something Something</h3>
<div>
<table class="generic small oddeven middle center">
<tr>
<th style="width: 24px;"></th>
<th>Something</th>
<th style="width: 270px;">Something Something</th>
@if (Model.Value3 != 0)
{
<th style="width: 70px;" class="dull right">Something</th>
}
<th style="width: 130px;"></th>
</tr>
@{
foreach (var data in Model.Data1)
{
<tr>
<td class="left">@data.Icon</td>
<td class="left">@data.Name</td>
<td style="text-align:center">@data.Html</td>
@if (Model.Value3 != 0)
{
<td class="dull right">
@(new TimeSpan(0, 0, (int)data.Seconds)) <br />
(@data.PerHour.ToString("N2") p/h)
</td>
}
<td>
<form asp-page-handler="Make">
<input type="hidden" name="Id" value="@((int)Model.Value3)" />
<input type="hidden" name="ResType" value="@data.Id" />
<input type="text" name="Quantity" style="width: 35px; font-size: 11px; text-align: left;" placeholder="@data.Max.ToString(System.Globalization.NumberFormatInfo.InvariantInfo)" />
<a href="#" class="max">max</a>
<button class="small">Make</button>
</form>
</td>
</tr>
}
}
</table>
</div>
<h3 style="clear:both;">Something something something</h3>
<div>
@{
if (Model.Data2.Count > 0)
{
<table class="oddeven" style="font-size: 11px;">
<tr>
<th style="width:20px"></th>
<th class="left">SomethingA</th>
<th style="width: 80px;">SomethingB</th>
<th style="width: 60px">SomethingC</th>
<th>SomethingD</th>
<th style="width: 60px">SomethingE</th>
<th style="width: 90px;">SomethingF</th>
<th style="width: 56px;"></th>
</tr>
@foreach (var data in Model.Data2)
{
var StartDate = data.StartDate;
var CompleteDate = data.CompleteDate;
<tr>
<td>@data.Icon</td>
<td class="left">@data.Name</td>
<td>@data.Value</td>
<td class="center">@StartDate.ToString("dd MMM HH:mm:ss")</td>
@{
float percentage = 100f;
var totalTime = CompleteDate.Subtract(StartDate).TotalMilliseconds;
if (totalTime > 1000)
{
percentage = 100f * (float)DateTimeOffset.UtcNow.Subtract(StartDate).TotalMilliseconds / (float)totalTime;
}
percentage = MathF.Min(100f, MathF.Max(0f, percentage));
var startDate = ToInt64(StartDate.Subtract(DateTime.UnixEpoch).Ticks / (double)10000);
var endDate = ToInt64(CompleteDate.Subtract(DateTime.UnixEpoch).Ticks / (double)10000);
}
<td>
<div class="progBar">
<div class="progProgress" data-start="@startDate" data-end="@endDate" style="width: @(percentage.ToString("N0"))%;"></div>
</div>
</td>
<td class="center">@CompleteDate.ToString("dd MMM HH:mm:ss")</td>
<td>
<span class="progTime" data-start="@startDate" data-end="@endDate">@(CompleteDate.Subtract(DateTime.UtcNow).ToString())</span>
</td>
<td>
<form method="post" asp-page-handler="Cancel">
<input type="hidden" name="id" value="@data.Id" />
<button class="small">Cancel</button>
</form>
</td>
</tr>
}
</table>
}
else
{
<span>Something @Model.Name something something something something.</span>
}
}
</div>

View File

@ -0,0 +1,35 @@
using Data;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Pages
{
public class PageA : Page
{
public int Value { get; } = 0;
public int Value1 { get; } = 0;
public int Value2 { get; } = 0;
public int Value3 { get; } = 1;
public bool Condition { get; } = true;
public string Name { get; } = "A Name";
public List<DataA> Data1 { get; }
public List<DataB> Data2 { get; }
public PageA(List<DataA> dataA, List<DataB> dataB, ILogger<PageA> logger) : base(logger)
{
Data1 = dataA;
Data2 = dataB;
}
public async Task OnGetAsync()
{
PageTitle = "PageA Title";
PageIcon = "sicon dialogue_pagea";
await Task.Delay(0);
}
}
}

View File

@ -0,0 +1,12 @@
@model Pages.Page
<div class="dialog-subcat">
<div data-url="/Category/PageA" title="PageA Title" class="sicon dialogue_pagea"></div>
<div data-url="/Category/PageB" title="PageB Title" class="sicon dialogue_pageb"></div>
<div data-url="/Category/PageC" title="PageC Title" class="sicon dialogue_pagec"></div>
<div data-url="/Category/PageD" title="PageD Title" class="sicon dialogue_paged"></div>
<div data-url="/Category/PageE" title="PageE Title" class="sicon dialogue_pagee"></div>
<div data-url="/Category/PageF" title="PageF Title" class="sicon dialogue_pagef"></div>
<div data-url="/Category/PageG" title="PageG Title" class="sicon dialogue_pageg"></div>
</div>
<div class="licon micon dialogue_category"></div>

View File

@ -0,0 +1,26 @@
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace Pages
{
public class Page : PageModel
{
public ILogger Logger { get; }
public string PageIcon { get; protected set; }
public string PageTitle { get; protected set; }
public string PageUrl { get; protected set; }
public Page(ILogger logger)
{
Logger = logger;
}
public void AddErrorMessage(string message)
{
Response.Headers.Add("X-Error-Message", UrlEncoder.Default.Encode(message));
}
}
}

View File

@ -0,0 +1,31 @@
@model Pages.Page
@RenderSection("Subcategories")
<div class="dialog-title-icon @Model.PageIcon"></div>
<div class="dialog-title">@Model.PageTitle</div>
@{
var hasTabs = IsSectionDefined("Tabs");
}
<div class="dialog-body@(hasTabs ? "-tabs" : "")">
@if (hasTabs)
{
<div class="dialog-tabs">
@RenderSection("Tabs", required: false)
</div>
}
<div class="dialog-parch-bottom@(hasTabs ? " tabs" : "")">
<div id="contentScroll" tabindex="20" class="dialog-parch-scroll@(hasTabs ? " tabs" : "")">
<div class="dialog-parch-left">
<div class="dialog-parch-right">
<div id="contentWindow" class="dialog-parch@(hasTabs ? " tabs" : "")">
@RenderBody()
</div>
</div>
</div>
</div>
<div class="dialog-parch-shadow@(hasTabs ? " tabs" : "")"></div>
</div>
</div>
<div class="dialog-close-icon icon_close"></div>
<div class="dialog-bottom-left-edge"></div>
<div class="dialog-bottom-edge"></div>

View File

@ -0,0 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
</PropertyGroup>
<!-- These references are used when running locally -->
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
</ItemGroup>
<!--
These references are used when running on the Benchmarks Server.
-->
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
<PackageReference Include="Microsoft.AspNetCore.App" Version="$(MicrosoftNETCoreApp22PackageVersion)" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
Url: /Category/PageA

View File

@ -0,0 +1,78 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Html;
using Microsoft.Extensions.Configuration;
using System.IO;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<List<DataA>>(_ => DataA);
services.AddScoped<List<DataB>>(_ => DataB);
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Latest);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
private static List<DataA> DataA = GenerateDataA();
private static List<DataA> GenerateDataA()
{
var dataA = new List<DataA>();
foreach (var i in Enumerable.Range(0, 100))
{
dataA.Add(new DataA(i, new HtmlString(i.ToString()), new HtmlString(i.ToString()), i.ToString(), i, i, 60f / i));
}
return dataA;
}
private static List<DataB> DataB = GenerateDataB();
private static List<DataB> GenerateDataB()
{
var utc = DateTimeOffset.UtcNow;
var dataB = new List<DataB>();
foreach (var i in Enumerable.Range(0, 100))
{
dataB.Add(new DataB(i, new HtmlString(i.ToString()), i.ToString(), i, utc, utc));
}
return dataB;
}
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
return new WebHostBuilder()
.UseKestrel()
.UseUrls("http://+:5000")
.UseConfiguration(configuration)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>();
}
}

View File

@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
var state = routeContext.RouteData.PushState(MockRouter.Instance, routeValues, null);
var actual = NaiveSelectCandiates(_actions, routeContext.RouteData.Values);
var actual = NaiveSelectCandidates(_actions, routeContext.RouteData.Values);
Verify(expected, actual);
state.Restore();
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
}
// A naive implementation we can use to generate match data for inputs, and for a baseline.
private static IReadOnlyList<ActionDescriptor> NaiveSelectCandiates(ActionDescriptor[] actions, RouteValueDictionary routeValues)
private static IReadOnlyList<ActionDescriptor> NaiveSelectCandidates(ActionDescriptor[] actions, RouteValueDictionary routeValues)
{
var results = new List<ActionDescriptor>();
for (var i = 0; i < actions.Length; i++)
@ -175,7 +175,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
{
var action = actions[i];
var routeValues = new RouteValueDictionary(action.RouteValues);
var matches = NaiveSelectCandiates(actions, routeValues);
var matches = NaiveSelectCandidates(actions, routeValues);
if (matches.Count == 0)
{
throw new InvalidOperationException("This should have at least one match.");
@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
// Make one of the route values not match.
routeValues[routeValues.First().Key] = ((string)routeValues.First().Value) + "fkdkfdkkf";
var matches = NaiveSelectCandiates(actions, routeValues);
var matches = NaiveSelectCandidates(actions, routeValues);
if (matches.Count != 0)
{
throw new InvalidOperationException("This should have 0 matches.");

View File

@ -48,8 +48,8 @@
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
<MicrosoftAspNetCoreResponseCachingPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-a-alpha1-link-generator-master-16955</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-a-alpha1-link-generator-master-16955</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreSessionPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreSessionPackageVersion>

View File

@ -36,6 +36,9 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
/// </summary>
public IList<IActionConstraintMetadata> ActionConstraints { get; set; }
/// <summary>
/// Gets or sets the endpoint metadata for this action.
/// </summary>
public IList<object> EndpointMetadata { get; set; }
/// <summary>

View File

@ -31,15 +31,11 @@ namespace Microsoft.AspNetCore.Mvc
/// <param name="actionContext">The <see cref="ActionContext"/> to copy.</param>
public ActionContext(ActionContext actionContext)
: this(
actionContext.HttpContext,
actionContext.RouteData,
actionContext.ActionDescriptor,
actionContext.ModelState)
actionContext?.HttpContext,
actionContext?.RouteData,
actionContext?.ActionDescriptor,
actionContext?.ModelState)
{
if (actionContext == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
}
/// <summary>
@ -136,4 +132,4 @@ namespace Microsoft.AspNetCore.Mvc
get; set;
}
}
}
}

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc
}
/// <summary>
/// Intializes a new instance of <see cref="ActionResult{TValue}"/> using the specified <see cref="ActionResult"/>.
/// Initializes a new instance of <see cref="ActionResult{TValue}"/> using the specified <see cref="ActionResult"/>.
/// </summary>
/// <param name="result">The <see cref="ActionResult"/>.</param>
public ActionResult(ActionResult result)

View File

@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc
}
else if (methods.Length > 1)
{
throw new ArgumentException(Resources.FormatApiConventionMethod_AmbigiousMethodName(methodName, conventionType), nameof(methodName));
throw new ArgumentException(Resources.FormatApiConventionMethod_AmbiguousMethodName(methodName, conventionType), nameof(methodName));
}
return methods[0];

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
/// Gets the list of <see cref="ApplicationPart"/> instances.
/// <para>
/// Instances in this collection are stored in precedence order. An <see cref="ApplicationPart"/> that appears
/// earlier in the list has a higher precendence.
/// earlier in the list has a higher precedence.
/// An <see cref="IApplicationFeatureProvider"/> may choose to use this an interface as a way to resolve conflicts when
/// multiple <see cref="ApplicationPart"/> instances resolve equivalent feature values.
/// </para>

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary>
/// <remarks>
/// By default, this maps to <see cref="ProblemDetails.Title"/> and should not change
/// between multiple occurences of the same error.
/// between multiple occurrences of the same error.
/// </remarks>
public string Title { get; set; }
}

View File

@ -6,11 +6,11 @@ using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc
{
/// <summary>
/// Specifies the version compatibility of runtime behaviors configured by <see cref="MvcOptions"/>.
/// Specifies the version compatibility of runtime behaviors configured by <see cref="MvcOptions"/>.
/// </summary>
/// <remarks>
/// <para>
/// The best way to set a compatibility version is by using
/// The best way to set a compatibility version is by using
/// <see cref="MvcCoreMvcBuilderExtensions.SetCompatibilityVersion"/> or
/// <see cref="MvcCoreMvcCoreBuilderExtensions.SetCompatibilityVersion"/> in your application's
/// <c>ConfigureServices</c> method.
@ -20,32 +20,32 @@ namespace Microsoft.AspNetCore.Mvc
/// public class Startup
/// {
/// ...
///
///
/// public void ConfigureServices(IServiceCollection services)
/// {
/// services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
/// }
///
///
/// ...
/// }
/// </code>
/// </example>
/// </para>
/// <para>
/// Setting compatiblity version to a specific version will change the default values of various
/// settings to match a particular minor release of ASP.NET Core MVC.
/// Setting compatibility version to a specific version will change the default values of various
/// settings to match a particular minor release of ASP.NET Core MVC.
/// </para>
/// </remarks>
public enum CompatibilityVersion
{
/// <summary>
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
/// ASP.NET Core MVC 2.0.
/// </summary>
Version_2_0,
/// <summary>
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
/// ASP.NET Core MVC 2.1.
/// </summary>
/// <remarks>

View File

@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.Mvc
// If there are multiple IConsumeActionConstraints which are defined at the class and
// at the action level, the one closest to the action overrides the others. To ensure this
// we take advantage of the fact that ConsumesAttribute is both an IActionFilter and an
// IConsumeActionConstraint. Since filterdescriptor collection is ordered (the last filter is the one
// IConsumeActionConstraint. Since FilterDescriptor collection is ordered (the last filter is the one
// closest to the action), we apply this constraint only if there is no IConsumeActionConstraint after this.
return actionDescriptor.FilterDescriptors.Last(
filter => filter.Filter is IConsumesActionConstraint).Filter == this;

View File

@ -16,9 +16,9 @@ namespace Microsoft.AspNetCore.Mvc
///
/// <code>
/// [HttpGet]
/// public ProductModel GetProduct([FromServices] IProductModelRequestService productModelReqest)
/// public ProductModel GetProduct([FromServices] IProductModelRequestService productModelRequest)
/// {
/// return productModelReqest.Value;
/// return productModelRequest.Value;
/// }
/// </code>
/// </example>

View File

@ -9,7 +9,7 @@ using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
/// <summary>
/// A base class for infrastructure that implements ASP.NET Core MVC's support for
/// A base class for infrastructure that implements ASP.NET Core MVC's support for
/// <see cref="CompatibilityVersion"/>. This is framework infrastructure and should not be used
/// by application code.
/// </summary>
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
throw new ArgumentNullException(nameof(options));
}
// Evaluate DefaultValues onces so subclasses don't have to cache.
// Evaluate DefaultValues once so subclasses don't have to cache.
var defaultValues = DefaultValues;
foreach (var @switch in options)

View File

@ -1,7 +1,6 @@
// 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.Collections.ObjectModel;
using System.Diagnostics;
@ -85,7 +84,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return new CompositeChangeToken(changeTokens);
}
private void Initialize()
{
// Using double-checked locking on initialization because we fire change token callbacks
@ -134,7 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
// Consumers who poll will observe a new action descriptor collection at step 2 - they will see
// the new collection and ignore the change token.
//
// Consumers who listen to the change token will requery at step 4 - they will see the new collection
// Consumers who listen to the change token will re-query at step 4 - they will see the new collection
// and new change token.
//
// Anyone who acquires the collection and change token between steps 2 and 3 will be notified of

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
///</para>
///<para>
/// To be reactively notified of changes, downcast to <see cref="ActionDescriptorCollectionProvider"/> and
/// subcribe to the change token returned from <see cref="ActionDescriptorCollectionProvider.GetChangeToken"/>
/// subscribe to the change token returned from <see cref="ActionDescriptorCollectionProvider.GetChangeToken"/>
/// using <see cref="ChangeToken.OnChange(System.Func{IChangeToken}, System.Action)"/>.
/// </para>
/// <para>

View File

@ -12,14 +12,14 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
/// </summary>
/// <typeparam name="TResult">The type of <see cref="IActionResult"/>.</typeparam>
/// <remarks>
/// Implementions of <see cref="IActionResultExecutor{TResult}"/> are typically called by the
/// Implementations of <see cref="IActionResultExecutor{TResult}"/> are typically called by the
/// <see cref="IActionResult.ExecuteResultAsync(ActionContext)"/> method of the corresponding action result type.
/// Implementations should be registered as singleton services.
/// </remarks>
public interface IActionResultExecutor<in TResult> where TResult : IActionResult
{
/// <summary>
/// Asynchronously excecutes the action result, by modifying the <see cref="HttpResponse"/>.
/// Asynchronously executes the action result, by modifying the <see cref="HttpResponse"/>.
/// </summary>
/// <param name="context">The <see cref="ActionContext"/> associated with the current request."/></param>
/// <param name="result">The action result to execute.</param>

View File

@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
=> typeof(Task<IActionResult>).IsAssignableFrom(executor.MethodReturnType);
}
// Task<PhysicalfileResult> DownloadFile(..)
// Task<PhysicalFileResult> DownloadFile(..)
// ValueTask<ViewResult> GetViewsAsync(..)
private class TaskOfActionResultExecutor : ActionMethodExecutor
{

View File

@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// We also want to add the same (as in reference equality) list of actions to the ordinal entries.
// We'll keep updating `entries` to include all of the actions in the same equivalence class -
// meaning, all conventionally routed actions for which the route values are equalignoring case.
// meaning, all conventionally routed actions for which the route values are equal ignoring case.
//
// `entries` will appear in `OrdinalIgnoreCaseEntries` exactly once and in `OrdinalEntries` once
// for each variation of casing that we've seen.

View File

@ -138,10 +138,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
ActionModel action)
{
var defaultControllerConstraints = Enumerable.Empty<IActionConstraintMetadata>();
var defaultControllerEndpointMetadata = Enumerable.Empty<object>();
if (controller.Selectors.Count > 0)
{
defaultControllerConstraints = controller.Selectors[0].ActionConstraints
.Where(constraint => !(constraint is IRouteTemplateProvider));
defaultControllerEndpointMetadata = controller.Selectors[0].EndpointMetadata;
}
var actionDescriptors = new List<ControllerActionDescriptor>();
@ -164,8 +166,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
AddActionConstraints(actionDescriptor, actionSelector, controllerConstraints);
// REVIEW: Need to get metadata from controller
actionDescriptor.EndpointMetadata = actionSelector.EndpointMetadata.ToList();
// Metadata for the action is more significant so order it before the controller metadata
var actionDescriptorMetadata = actionSelector.EndpointMetadata.ToList();
actionDescriptorMetadata.AddRange(defaultControllerEndpointMetadata);
actionDescriptor.EndpointMetadata = actionDescriptorMetadata;
}
return actionDescriptors;

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public RequestDelegate GetPipeline(Type configurationType)
{
// Build the pipeline only once. This is similar to how middlewares registered in Startup are constructed.
// Build the pipeline only once. This is similar to how middleware registered in Startup are constructed.
var requestDelegate = _pipelinesCache.GetOrAdd(
configurationType,
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
// Ideally we want the experience of a middleware pipeline to behave the same as if it was registered
// in Startup. In this scenario, an Exception thrown in a middelware later in the pipeline gets
// in Startup. In this scenario, an Exception thrown in a middleware later in the pipeline gets
// propagated back to earlier middleware. So, check if a later resource filter threw an Exception and
// propagate that back to the middleware pipeline.
resourceExecutedContext.ExceptionDispatchInfo?.Throw();

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private readonly DefaultHttpContext _httpContextInstance;
// The following are protected by this lock for WRITES only. This pattern is similar
// to DefaultActionDescriptorChangeProvider - see comments there for details on
// to DefaultActionDescriptorChangeProvider - see comments there for details on
// all of the threading behaviors.
private readonly object _lock = new object();
private List<Endpoint> _endpoints;
@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// - /Home/Index/{id?}
// - /Home
// - /
if (UseDefaultValuePlusRemainingSegementsOptional(i, action, endpointInfo, newPathSegments))
if (UseDefaultValuePlusRemainingSegmentsOptional(i, action, endpointInfo, newPathSegments))
{
var subPathSegments = newPathSegments.Take(i);
@ -270,7 +270,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
}
private bool UseDefaultValuePlusRemainingSegementsOptional(
private bool UseDefaultValuePlusRemainingSegmentsOptional(
int segmentIndex,
ActionDescriptor action,
MvcEndpointInfo endpointInfo,

View File

@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
}
// If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since
// that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyColection<> and
// that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyCollection<> and
// IReadOnlyList<>).
var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
if (enumerableType != null)

View File

@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
//
// If a property does not have a binding source, then it's fair game for any value provider.
//
// If any property meets the above conditions and has a value from valueproviders, then we'll
// If any property meets the above conditions and has a value from ValueProviders, then we'll
// create the model and try to bind it. OR if ALL properties of the model have a greedy source,
// then we go ahead and create it.
//

View File

@ -82,11 +82,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 }
//
// Valid Scenarios:
// 1. valueproviderresult="Value2,Value4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
// 2. valueproviderresult="2,4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
// 1. valueProviderResult="Value2,Value4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
// 2. valueProviderResult="2,4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
//
// Invalid Scenarios:
// 1. valueproviderresult="2,10", model=12, underlying=12, converted=12
// 1. valueProviderResult="2,10", model=12, underlying=12, converted=12
//
var underlying = Convert.ChangeType(
model,

View File

@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var headerName = bindingContext.FieldName;
// Do not set ModelBindingResult to Failed on not finding the value in the header as we want the inner
// modelbinder to do that. This would give a chance to the inner binder to add more useful information.
// ModelBinder to do that. This would give a chance to the inner binder to add more useful information.
// For example, SimpleTypeModelBinder adds a model error when binding to let's say an integer and the
// model is null.
var request = bindingContext.HttpContext.Request;

View File

@ -9,7 +9,7 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// Normalizes keys, in a keyvaluepair collection, from jQuery format to a format that MVC understands.
// Normalizes keys, in a KeyValuePair collection, from jQuery format to a format that MVC understands.
internal static class JQueryKeyValuePairNormalizer
{
public static IDictionary<string, StringValues> GetValues(

View File

@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var cacheEntry = GetCacheEntry(modelType);
// We're relying on a safe race-condition for Properties - take care only
// to set the value onces the properties are fully-initialized.
// to set the value once the properties are fully-initialized.
if (cacheEntry.Details.Properties == null)
{
var key = ModelMetadataIdentity.ForType(modelType);

View File

@ -1509,18 +1509,18 @@ namespace Microsoft.AspNetCore.Mvc.Core
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConvention_UnsupportedAttributesOnConvention"), p0, p1, p2);
/// <summary>
/// Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.
/// Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.
/// </summary>
internal static string ApiConventionMethod_AmbigiousMethodName
internal static string ApiConventionMethod_AmbiguousMethodName
{
get => GetString("ApiConventionMethod_AmbigiousMethodName");
get => GetString("ApiConventionMethod_AmbiguousMethodName");
}
/// <summary>
/// Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.
/// Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.
/// </summary>
internal static string FormatApiConventionMethod_AmbigiousMethodName(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_AmbigiousMethodName"), p0, p1);
internal static string FormatApiConventionMethod_AmbiguousMethodName(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_AmbiguousMethodName"), p0, p1);
/// <summary>
/// A method named '{0}' was not found on convention type '{1}'.

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -451,8 +451,8 @@
<data name="ApiConvention_UnsupportedAttributesOnConvention" xml:space="preserve">
<value>Method {0} is decorated with the following attributes that are not allowed on an API convention method:{1}The following attributes are allowed on API convention methods: {2}.</value>
</data>
<data name="ApiConventionMethod_AmbigiousMethodName" xml:space="preserve">
<value>Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.</value>
<data name="ApiConventionMethod_AmbiguousMethodName" xml:space="preserve">
<value>Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.</value>
</data>
<data name="ApiConventionMethod_NoMethodFound" xml:space="preserve">
<value>A method named '{0}' was not found on convention type '{1}'.</value>

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
@ -51,46 +52,41 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(urlActionContext));
}
var valuesDictionary = GetValuesDictionary(urlActionContext.Values);
var values = GetValuesDictionary(urlActionContext.Values);
if (urlActionContext.Action == null)
{
if (!valuesDictionary.ContainsKey("action") &&
if (!values.ContainsKey("action") &&
AmbientValues.TryGetValue("action", out var action))
{
valuesDictionary["action"] = action;
values["action"] = action;
}
}
else
{
valuesDictionary["action"] = urlActionContext.Action;
values["action"] = urlActionContext.Action;
}
if (urlActionContext.Controller == null)
{
if (!valuesDictionary.ContainsKey("controller") &&
if (!values.ContainsKey("controller") &&
AmbientValues.TryGetValue("controller", out var controller))
{
valuesDictionary["controller"] = controller;
values["controller"] = controller;
}
}
else
{
valuesDictionary["controller"] = urlActionContext.Controller;
values["controller"] = urlActionContext.Controller;
}
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
valuesDictionary,
out var link);
if (!successfullyGeneratedLink)
{
//TODO: log here
return null;
}
return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, link, urlActionContext.Fragment);
routeName: null,
values,
new FragmentString(urlActionContext.Fragment == null ? null : "#" + urlActionContext.Fragment));
return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, path);
}
/// <inheritdoc />
@ -101,20 +97,12 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(routeContext));
}
var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
routeContext.RouteName,
valuesDictionary,
out var link);
if (!successfullyGeneratedLink)
{
return null;
}
return GenerateUrl(routeContext.Protocol, routeContext.Host, link, routeContext.Fragment);
routeContext.Values,
new FragmentString(routeContext.Fragment == null ? null : "#" + routeContext.Fragment));
return GenerateUrl(routeContext.Protocol, routeContext.Host, path);
}
}
}

View File

@ -201,6 +201,68 @@ namespace Microsoft.AspNetCore.Mvc.Routing
}
}
/// <summary>
/// Generates a URI from the provided components.
/// </summary>
/// <param name="protocol">The URI scheme/protocol.</param>
/// <param name="host">The URI host.</param>
/// <param name="path">The URI path and remaining portions (path, query, and fragment).</param>
/// <returns>
/// An absolute URI if the <paramref name="protocol"/> or <paramref name="host"/> is specified, otherwise generates a
/// URI with an absolute path.
/// </returns>
protected string GenerateUrl(string protocol, string host, string path)
{
// This method is similar to GenerateUrl, but it's used for EndpointRouting. It ignores pathbase and fragment
// because those have already been incorporated.
if (path == null)
{
return null;
}
// Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
// In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
// For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
string url;
if (TryFastGenerateUrl(protocol, host, path, fragment: null, out url))
{
return url;
}
var builder = GetStringBuilder();
try
{
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
{
AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
// We're returning a partial URL (just path + query + fragment), but we still want it to be rooted.
if (builder.Length == 0 || builder[0] != '/')
{
builder.Insert(0, '/');
}
}
else
{
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
builder.Append(protocol);
builder.Append("://");
host = string.IsNullOrEmpty(host) ? ActionContext.HttpContext.Request.Host.Value : host;
builder.Append(host);
AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
}
return builder.ToString();
}
finally
{
// Clear the StringBuilder so that it can reused for the next call.
builder.Clear();
}
}
// for unit testing
internal static void AppendPathAndFragment(StringBuilder builder, PathString pathBase, string virtualPath, string fragment)
{

View File

@ -462,7 +462,7 @@ namespace Microsoft.AspNetCore.Mvc
if (!routeValues.ContainsKey("handler") &&
ambientValues.TryGetValue("handler", out var handler))
{
// Clear out formaction unless it's explicitly specified in the routeValues.
// Clear out form action unless it's explicitly specified in the routeValues.
routeValues["handler"] = null;
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc
public class ValidationProblemDetails : ProblemDetails
{
/// <summary>
/// Intializes a new instance of <see cref="ValidationProblemDetails"/>.
/// Initializes a new instance of <see cref="ValidationProblemDetails"/>.
/// </summary>
public ValidationProblemDetails()
{

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc
}
/// <summary>
/// Gets or sets a flag to determine whether error messsages from JSON deserialization by the
/// Gets or sets a flag to determine whether error messages from JSON deserialization by the
/// <see cref="JsonInputFormatter"/> will be added to the <see cref="ModelStateDictionary"/>. The default
/// value is <c>false</c>, meaning that a generic error message will be used instead.
/// </summary>

View File

@ -25,12 +25,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
if (duplicates != null)
{
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.Identifier));
var viewsDifferingInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.Identifier));
var message = string.Join(
Environment.NewLine,
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
viewsDiffereningInCase);
viewsDifferingInCase);
throw new InvalidOperationException(message);
}

View File

@ -35,12 +35,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
{
// Ensure parts do not specify views with differing cases. This is not supported
// at runtime and we should flag at as such for precompiled views.
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
var viewsDifferingInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
var message = string.Join(
Environment.NewLine,
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
viewsDiffereningInCase);
viewsDifferingInCase);
throw new InvalidOperationException(message);
}

View File

@ -81,7 +81,7 @@ namespace Microsoft.Extensions.DependencyInjection
// ViewFeature items have precedence semantics - when two views have the same path \ identifier,
// the one that appears earlier in the list wins. Therefore the ordering of
// RazorCompiledItemFeatureProvider and ViewsFeatureProvider is pertinent - any view compiled
// using the Sdk will be prefered to views compiled using MvcPrecompilation.
// using the Sdk will be preferred to views compiled using MvcPrecompilation.
if (!builder.PartManager.FeatureProviders.OfType<RazorCompiledItemFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider());
@ -159,6 +159,9 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
services.TryAddSingleton<
IRazorViewEngineFileProviderAccessor,
DefaultRazorViewEngineFileProviderAccessor>();

View File

@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// from either the set of known precompiled views, or by being compiled.
_cache = cache;
// We need to validate that the all of the precompiled views are unique by path (case-insenstive).
// We need to validate that the all of the precompiled views are unique by path (case-insensitive).
// We do this because there's no good way to canonicalize paths on windows, and it will create
// problems when deploying to linux. Rather than deal with these issues, we just don't support
// views that differ only by case.
@ -116,6 +116,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
}
public bool AllowRecompilingViewsOnFileChange { get; set; }
/// <inheritdoc />
public Task<CompiledViewDescriptor> CompileAsync(string relativePath)
{
@ -254,16 +256,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Used to validate and recompile
NormalizedPath = normalizedPath,
ExpirationTokens = new List<IChangeToken>(),
};
var checksums = precompiledView.Item.GetChecksumMetadata();
for (var i = 0; i < checksums.Count; i++)
{
// We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job,
// so it probably will.
item.ExpirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier));
}
ExpirationTokens = GetExpirationTokens(precompiledView),
};
// We also need to create a new descriptor, because the original one doesn't have expiration tokens on
// it. These will be used by the view location cache, which is like an L1 cache for views (this class is
@ -282,10 +277,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private ViewCompilerWorkItem CreateRuntimeCompilationWorkItem(string normalizedPath)
{
var expirationTokens = new List<IChangeToken>()
IList<IChangeToken> expirationTokens = Array.Empty<IChangeToken>();
if (AllowRecompilingViewsOnFileChange)
{
_fileProvider.Watch(normalizedPath),
};
var changeToken = _fileProvider.Watch(normalizedPath);
expirationTokens = new List<IChangeToken> { changeToken };
}
var projectItem = _projectEngine.FileSystem.GetItem(normalizedPath);
if (!projectItem.Exists)
@ -293,7 +291,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
_logger.ViewCompilerCouldNotFindFileAtPath(normalizedPath);
// If the file doesn't exist, we can't do compilation right now - we still want to cache
// the fact that we tried. This will allow us to retrigger compilation if the view file
// the fact that we tried. This will allow us to re-trigger compilation if the view file
// is added.
return new ViewCompilerWorkItem()
{
@ -313,9 +311,46 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
_logger.ViewCompilerFoundFileToCompile(normalizedPath);
GetChangeTokensFromImports(expirationTokens, projectItem);
return new ViewCompilerWorkItem()
{
SupportsCompilation = true,
NormalizedPath = normalizedPath,
ExpirationTokens = expirationTokens,
};
}
private IList<IChangeToken> GetExpirationTokens(CompiledViewDescriptor precompiledView)
{
if (!AllowRecompilingViewsOnFileChange)
{
return Array.Empty<IChangeToken>();
}
var checksums = precompiledView.Item.GetChecksumMetadata();
var expirationTokens = new List<IChangeToken>(checksums.Count);
for (var i = 0; i < checksums.Count; i++)
{
// We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job,
// so it probably will.
expirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier));
}
return expirationTokens;
}
private void GetChangeTokensFromImports(IList<IChangeToken> expirationTokens, RazorProjectItem projectItem)
{
if (!AllowRecompilingViewsOnFileChange)
{
return;
}
// OK this means we can do compilation. For now let's just identify the other files we need to watch
// so we can create the cache entry. Compilation will happen after we release the lock.
var importFeature = _projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
// There should always be an import feature unless someone has misconfigured their RazorProjectEngine.
@ -330,14 +365,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
expirationTokens.Add(_fileProvider.Watch(physicalImport.FilePath));
}
return new ViewCompilerWorkItem()
{
SupportsCompilation = true,
NormalizedPath = normalizedPath,
ExpirationTokens = expirationTokens,
};
}
protected virtual CompiledViewDescriptor CompileAndEmit(string relativePath)

View File

@ -80,7 +80,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
#pragma warning restore CS0618 // Type or member is obsolete
feature.ViewDescriptors,
_compilationMemoryCacheProvider.CompilationMemoryCache,
_logger);
_logger)
{
AllowRecompilingViewsOnFileChange = _viewEngineOptions.AllowRecompilingViewsOnFileChange,
};
}
}
}

View File

@ -7,3 +7,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.FileProviders;
@ -13,10 +15,21 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// <summary>
/// Provides programmatic configuration for the <see cref="RazorViewEngine"/>.
/// </summary>
public class RazorViewEngineOptions
public class RazorViewEngineOptions : IEnumerable<ICompatibilitySwitch>
{
private readonly ICompatibilitySwitch[] _switches;
private readonly CompatibilitySwitch<bool> _allowRecompilingViewsOnFileChange;
private Action<RoslynCompilationContext> _compilationCallback = c => { };
public RazorViewEngineOptions()
{
_allowRecompilingViewsOnFileChange = new CompatibilitySwitch<bool>(nameof(AllowRecompilingViewsOnFileChange));
_switches = new[]
{
_allowRecompilingViewsOnFileChange,
};
}
/// <summary>
/// Gets a <see cref="IList{IViewLocationExpander}"/> used by the <see cref="RazorViewEngine"/>.
/// </summary>
@ -181,5 +194,52 @@ namespace Microsoft.AspNetCore.Mvc.Razor
_compilationCallback = value;
}
}
/// <summary>
/// Gets or sets a value that determines if Razor files (Razor Views and Razor Pages) are recompiled and updated
/// if files change on disk.
/// <para>
/// When <see langword="true"/>, MVC will use <see cref="IFileProvider.Watch(string)"/> to watch for changes to
/// Razor files in configured <see cref="IFileProvider"/> instances.
/// </para>
/// </summary>
/// <value>
/// The default value is <see langword="true"/> if the version is <see cref = "CompatibilityVersion.Version_2_1" />
/// or earlier. If the version is later and <see cref= "IHostingEnvironment.EnvironmentName" /> is <c>Development</c>,
/// the default value is <see langword="true"/>. Otherwise, the default value is <see langword="false" />.
/// </value>
/// <remarks>
/// <para>
/// This property is associated with a compatibility switch and can provide a different behavior depending on
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
/// guidance and examples of setting the application's compatibility version.
/// </para>
/// <para>
/// Configuring the desired value of the compatibility switch by calling this property's setter will take
/// precedence over the value implied by the application's <see cref="CompatibilityVersion"/>.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
/// lower then this setting will have the value <see langword="true"/> unless explicitly configured.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_2"/> or
/// higher then this setting will have the value <see langword="false"/> unless
/// <see cref="IHostingEnvironment.EnvironmentName"/> is <c>Development</c> or the value is explicitly configured.
/// </para>
/// </remarks>
public bool AllowRecompilingViewsOnFileChange
{
// Note: When compatibility switches are removed in 3.0, this property should be retained as a regular boolean property.
get => _allowRecompilingViewsOnFileChange.Value;
set => _allowRecompilingViewsOnFileChange.Value = value;
}
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
}
}

View File

@ -2,27 +2,45 @@
// 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 Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
namespace Microsoft.AspNetCore.Mvc.Razor
{
/// <summary>
/// Sets up default options for <see cref="RazorViewEngineOptions"/>.
/// </summary>
public class RazorViewEngineOptionsSetup : IConfigureOptions<RazorViewEngineOptions>
internal class RazorViewEngineOptionsSetup :
ConfigureCompatibilityOptions<RazorViewEngineOptions>,
IConfigureOptions<RazorViewEngineOptions>
{
private readonly IHostingEnvironment _hostingEnvironment;
/// <summary>
/// Initializes a new instance of <see cref="RazorViewEngineOptions"/>.
/// </summary>
/// <param name="hostingEnvironment"><see cref="IHostingEnvironment"/> for the application.</param>
public RazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment)
public RazorViewEngineOptionsSetup(
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
var values = new Dictionary<string, object>();
if (Version < CompatibilityVersion.Version_2_2)
{
// Default to true in 2.1 or earlier. In 2.2, we have to conditionally enable this
// and consequently this switch has no default value.
values[nameof(RazorViewEngineOptions.AllowRecompilingViewsOnFileChange)] = true;
}
return values;
}
}
public void Configure(RazorViewEngineOptions options)
{
if (options == null)
@ -41,6 +59,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
options.AreaViewLocationFormats.Add("/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
if (_hostingEnvironment.IsDevelopment())
{
options.AllowRecompilingViewsOnFileChange = true;
}
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.FileProviders;
@ -18,11 +19,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private readonly IFileProvider _fileProvider;
private readonly string[] _searchPatterns;
private readonly string[] _additionalFilesToTrack;
private readonly bool _watchForChanges;
public PageActionDescriptorChangeProvider(
RazorTemplateEngine templateEngine,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
IOptions<RazorPagesOptions> razorPagesOptions)
IOptions<RazorPagesOptions> razorPagesOptions,
IOptions<RazorViewEngineOptions> razorViewEngineOptions)
{
if (templateEngine == null)
{
@ -39,6 +42,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
throw new ArgumentNullException(nameof(razorPagesOptions));
}
_watchForChanges = razorViewEngineOptions.Value.AllowRecompilingViewsOnFileChange;
if (!_watchForChanges)
{
// No need to do any additional work if we aren't going to be watching for file changes.
return;
}
_fileProvider = fileProviderAccessor.FileProvider;
var rootDirectory = razorPagesOptions.Value.RootDirectory;
@ -84,6 +94,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public IChangeToken GetChangeToken()
{
if (!_watchForChanges)
{
return NullChangeToken.Singleton;
}
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + _searchPatterns.Length];
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
{

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting(PageRouteModelProviderContext context)
{
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectoryy = /Areas;
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectory = /Areas;
// we need to ensure that the page is only route-able via the area route. By adding area routes first,
// we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check.
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static bool IsRouteable(RazorProjectItem item)
{
// Pages like _ViewImports should not be routable.
// Pages like _ViewImports should not be routeable.
return !item.FileName.StartsWith("_", StringComparison.OrdinalIgnoreCase);
}
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
public class CacheTagKey : IEquatable<CacheTagKey>
{
private static readonly char[] AttributeSeparator = new[] { ',' };
private static readonly Func<IRequestCookieCollection, string, string> CookieAcccessor = (c, key) => c[key];
private static readonly Func<IRequestCookieCollection, string, string> CookieAccessor = (c, key) => c[key];
private static readonly Func<IHeaderDictionary, string, string> HeaderAccessor = (c, key) => c[key];
private static readonly Func<IQueryCollection, string, string> QueryAccessor = (c, key) => c[key];
private static readonly Func<RouteValueDictionary, string, string> RouteValueAccessor = (c, key) => c[key]?.ToString();
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
_expiresOn = tagHelper.ExpiresOn;
_expiresSliding = tagHelper.ExpiresSliding;
_varyBy = tagHelper.VaryBy;
_cookies = ExtractCollection(tagHelper.VaryByCookie, request.Cookies, CookieAcccessor);
_cookies = ExtractCollection(tagHelper.VaryByCookie, request.Cookies, CookieAccessor);
_headers = ExtractCollection(tagHelper.VaryByHeader, request.Headers, HeaderAccessor);
_queries = ExtractCollection(tagHelper.VaryByQuery, request.Query, QueryAccessor);
_routeValues = ExtractCollection(tagHelper.VaryByRoute, tagHelper.ViewContext.RouteData.Values, RouteValueAccessor);

View File

@ -141,7 +141,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// can't be put inside a using block.
entry.Dispose();
// Set the result on the TCS once we've commited the entry to the cache since commiting to the cache
// Set the result on the TCS once we've committed the entry to the cache since commiting to the cache
// may throw.
tcs.SetResult(content);
return content;

View File

@ -167,9 +167,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
}
/// <inheritdoc />
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
/// <remarks>Does nothing if user provides an <c>FormAction</c> attribute.</remarks>
/// <exception cref="InvalidOperationException">
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
/// Thrown if <c>FormAction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
/// <see cref="Fragment"/> or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
/// are non-<c>null</c>
@ -186,7 +186,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
throw new ArgumentNullException(nameof(output));
}
// If "formaction" is already set, it means the user is attempting to use a normal button or input element.
// If "FormAction" is already set, it means the user is attempting to use a normal button or input element.
if (output.Attributes.ContainsName(FormAction))
{
if (Action != null ||
@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
Route != null ||
(_routeValues != null && _routeValues.Count > 0))
{
// User specified a formaction and one of the bound attributes; can't override that formaction
// User specified a FormAction and one of the bound attributes; can't override that FormAction
// attribute.
throw new InvalidOperationException(
Resources.FormatFormActionTagHelper_CannotOverrideFormAction(

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
public class RenderAtEndOfFormTagHelper : TagHelper
{
// This TagHelper's order must be greater than the FormTagHelper's. I.e it must be executed after
// FormTaghelper does.
// FormTagHelper does.
/// <inheritdoc />
public override int Order => -900;

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var copiedAttribute = false;
// We iterate context.AllAttributes backwards since we prioritize TagHelperOutput values occurring
// before the current context.AllAttribtes[i].
// before the current context.AllAttributes[i].
for (var i = context.AllAttributes.Count - 1; i >= 0; i--)
{
// We look for the original attribute so we can restore the exact attribute name the user typed in

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers
/// <summary>
/// Creates a new instance of <see cref="RedirectHandler"/>.
/// </summary>
/// <param name="maxRedirects">The maximun number of redirect responses to follow. It must be
/// <param name="maxRedirects">The maximum number of redirect responses to follow. It must be
/// equal or greater than 0.</param>
public RedirectHandler(int maxRedirects)
{

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
/// <see cref="WebApplicationFactoryContentRootAttribute"/> on the assembly containing the functional tests with
/// a key equal to the <typeparamref name="TEntryPoint"/> assembly <see cref="Assembly.FullName"/>.
/// In case an attribute with the right key can't be found, <see cref="WebApplicationFactory{TEntryPoint}"/>
/// will fall back to searching for a solution file (*.sln) and then appending <typeparamref name="TEntryPoint"/> asembly name
/// will fall back to searching for a solution file (*.sln) and then appending <typeparamref name="TEntryPoint"/> assembly name
/// to the solution directory. The application root directory will be used to discover views and content files.
/// </para>
/// <para>

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc
/// Gets or sets <see cref="ViewDataDictionary"/> used by <see cref="ViewResult"/> and <see cref="ViewBag"/>.
/// </summary>
/// <remarks>
/// By default, this property is intiailized when <see cref="Controllers.IControllerActivator"/> activates
/// By default, this property is initialized when <see cref="Controllers.IControllerActivator"/> activates
/// controllers.
/// <para>
/// This property can be accessed after the controller has been activated, for example, in a controller action

View File

@ -5,6 +5,7 @@ using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@ -28,14 +29,30 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
// Don't do anything. We'll call FlushAsync.
}
public override async Task FlushAsync()
public override Task FlushAsync() => FlushAsyncCore();
// private non-virtual for internal calling.
// It first does a fast check to see if async is necessary, we inline this check.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task FlushAsyncCore()
{
var length = _charBuffer.Length;
if (length == 0)
{
return;
// If nothing sync buffered return CompletedTask,
// so we can fast-path skip async state-machine creation
return Task.CompletedTask;
}
return FlushAsyncAwaited();
}
private async Task FlushAsyncAwaited()
{
var length = _charBuffer.Length;
Debug.Assert(length > 0);
var pages = _charBuffer.Pages;
var count = pages.Count;
for (var i = 0; i < count; i++)
@ -89,21 +106,54 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
_charBuffer.Append(value);
}
public override async Task WriteAsync(char value)
public override Task WriteAsync(char value)
{
await FlushAsync();
var flushTask = FlushAsyncCore();
// FlushAsyncCore will return CompletedTask if nothing sync buffered
// Fast-path and skip async state-machine if only a single async operation
return ReferenceEquals(flushTask, Task.CompletedTask) ?
_inner.WriteAsync(value) :
WriteAsyncAwaited(flushTask, value);
}
private async Task WriteAsyncAwaited(Task flushTask, char value)
{
await flushTask;
await _inner.WriteAsync(value);
}
public override async Task WriteAsync(char[] buffer, int index, int count)
public override Task WriteAsync(char[] buffer, int index, int count)
{
await FlushAsync();
var flushTask = FlushAsyncCore();
// FlushAsyncCore will return CompletedTask if nothing sync buffered
// Fast-path and skip async state-machine if only a single async operation
return ReferenceEquals(flushTask, Task.CompletedTask) ?
_inner.WriteAsync(buffer, index, count) :
WriteAsyncAwaited(flushTask, buffer, index, count);
}
private async Task WriteAsyncAwaited(Task flushTask, char[] buffer, int index, int count)
{
await flushTask;
await _inner.WriteAsync(buffer, index, count);
}
public override async Task WriteAsync(string value)
public override Task WriteAsync(string value)
{
await FlushAsync();
var flushTask = FlushAsyncCore();
// FlushAsyncCore will return CompletedTask if nothing sync buffered
// Fast-path and skip async state-machine if only a single async operation
return ReferenceEquals(flushTask, Task.CompletedTask) ?
_inner.WriteAsync(value) :
WriteAsyncAwaited(flushTask, value);
}
private async Task WriteAsyncAwaited(Task flushTask, string value)
{
await flushTask;
await _inner.WriteAsync(value);
}

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
}
/// <inheritdoc />
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IHtmlContentBuilder Append(string unencoded)
{
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
}
/// <inheritdoc />
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IHtmlContentBuilder AppendHtml(IHtmlContent content)
{
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
}
/// <inheritdoc />
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IHtmlContentBuilder AppendHtml(string encoded)
{
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
return this;
}
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AppendValue(ViewBufferValue value)
{
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
page.Append(value);
}
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ViewBufferPage GetCurrentPage()
{

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
public bool IsFull => Count == Capacity;
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Append(ViewBufferValue value) => Buffer[Count++] = value;
}

View File

@ -16,13 +16,13 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public SaveTempDataAttribute()
{
// Since SaveTempDataFilter registers for a response's OnStarting callback, we want this filter to run
// as early as possible to get the opportunity to register the call back before any other result filter
// Since SaveTempDataFilter registers for a response's OnStarting callback, we want this filter to run
// as early as possible to get the opportunity to register the call back before any other result filter
// starts writing to the response stream.
Order = int.MinValue + 100;
}
// <inheritdoc />
/// <inheritdoc />
public int Order { get; set; }
/// <inheritdoc />

View File

@ -1565,10 +1565,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
}
[Fact]
public void GetApiDescription_ParameterDescription_RedundentMetadata_NotMergedWithParent()
public void GetApiDescription_ParameterDescription_RedundantMetadata_NotMergedWithParent()
{
// Arrange
var action = CreateActionDescriptor(nameof(AcceptsRedundentMetadata));
var action = CreateActionDescriptor(nameof(AcceptsRedundantMetadata));
var parameterDescriptor = action.Parameters.Single();
// Act
@ -1590,7 +1590,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
}
[Fact]
public void GetApiDescription_ParameterDescription_RedundentMetadata_WithParameterMetadata()
public void GetApiDescription_ParameterDescription_RedundantMetadata_WithParameterMetadata()
{
// Arrange
var action = CreateActionDescriptor(nameof(AcceptsPerson));
@ -2081,7 +2081,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
{
}
private void AcceptsRedundentMetadata([FromQuery] RedundentMetadata r)
private void AcceptsRedundantMetadata([FromQuery] RedundentMetadata r)
{
}

View File

@ -219,7 +219,7 @@ namespace Microsoft.AspNetCore.Mvc
}
[Fact]
public void OnFormatting_NullUrlHelperContextNoRequestServices_ThrowsArgumentNullExeption()
public void OnFormatting_NullUrlHelperContextNoRequestServices_ThrowsArgumentNullException()
{
// Arrange
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());

View File

@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Mvc
public void Convert_InfersDeclaredTypeFromActionResultTypeParameter()
{
// Arrange
var value = new DeriviedItem();
var value = new DerivedItem();
var actionResultOfT = new ActionResult<BaseItem>(value);
var convertToActionResult = (IConvertToActionResult)actionResultOfT;
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc
{
}
private class DeriviedItem : BaseItem
private class DerivedItem : BaseItem
{
}
}

View File

@ -85,14 +85,14 @@ namespace Microsoft.AspNetCore.Mvc
}
[Fact]
public void Constructor_ThrowsIfMethodIsAmbigous()
public void Constructor_ThrowsIfMethodIsAmbiguous()
{
// Arrange
var methodName = typeof(ConventionWithProducesAttribute).FullName + '.' + nameof(ConventionWithProducesAttribute.Get);
var attribute = typeof(ProducesAttribute);
var type = typeof(TestConventions);
var expected = $"Method name 'Method' is ambigous for convention type '{type}'. More than one method found with the name 'Method'.";
var expected = $"Method name 'Method' is ambiguous for convention type '{type}'. More than one method found with the name 'Method'.";
// Act & Assert
ExceptionAssert.ThrowsArgument(

View File

@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
}
[Fact]
public void IsNameMatch_WithPrefix_ReturnsFalse_IfNameIsNotProperPrfix()
public void IsNameMatch_WithPrefix_ReturnsFalse_IfNameIsNotProperPrefix()
{
// Arrange
var name = "Postman";
@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
}
[Fact]
public void IsMatch_ReturnsTrue_IfMethodNameAndParametersMatchs()
public void IsMatch_ReturnsTrue_IfMethodNameAndParametersMatches()
{
// Arrange
var method = typeof(TestController).GetMethod(nameof(TestController.Get));

View File

@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Mvc
[InlineData("application/json")]
[InlineData("application/json;Parameter1=12")]
[InlineData("text/xml")]
public void ActionConstraint_Accept_MatchesForMachingRequestContentType(string contentType)
public void ActionConstraint_Accept_MatchesForMatchingRequestContentType(string contentType)
{
// Arrange
var constraint = new ConsumesAttribute("application/json", "text/xml");

View File

@ -1027,10 +1027,10 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
// Arrange
var controller = new TestableController();
var pageName = "/Page-Name";
var routeVaues = new { key = "value" };
var routeValues = new { key = "value" };
// Act
var result = controller.RedirectToPage(pageName, routeVaues);
var result = controller.RedirectToPage(pageName, routeValues);
// Assert
Assert.IsType<RedirectToPageResult>(result);

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Pipeline1.ConfigurePipeline = (ab) =>
{
configureCallCount++;
ab.Use((httpCtxt, next) =>
ab.Use((httpContext, next) =>
{
return next();
});

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
httpContext.Setup(c => c.Request.Query["format"]).Returns("xml");
// Routedata contains json
// RouteData contains json
var data = new RouteData();
data.Values.Add("format", "json");

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
get
{
// object value, bool useDeclaredTypeAsString, bool expectedCanwriteResult, bool useNonNullContentType
// object value, bool useDeclaredTypeAsString, bool expectedCanWriteResult, bool useNonNullContentType
yield return new object[] { "valid value", true, false, true };
yield return new object[] { "valid value", false, false, true };
yield return new object[] { "", false, false, true };

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
}
[Fact]
public void Constructor_WithNameAndInitalValue_IsValueSetIsFalse()
public void Constructor_WithNameAndInitialValue_IsValueSetIsFalse()
{
// Arrange & Act
var @switch = new CompatibilitySwitch<bool>("TestProperty", initialValue: true);
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var @switch = new CompatibilitySwitch<bool>("TestProperty");
// Act
@switch.Value = false; // You don't need to actually change the value, just caling the setting works
@switch.Value = false; // You don't need to actually change the value, just calling the setting works
// Assert
Assert.False(@switch.Value);

View File

@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
}
[Fact]
public void ActionDescriptors_UpdatesAndResubscripes_WhenChangeTokenTriggers()
public void ActionDescriptors_UpdatesAndResubscribes_WhenChangeTokenTriggers()
{
// Arrange
var actionDescriptorProvider = new Mock<IActionDescriptorProvider>();

View File

@ -8,7 +8,7 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
public class ProblemDetalsClientErrorFactoryTest
public class ProblemDetailsClientErrorFactoryTest
{
[Fact]
public void GetClientError_ReturnsProblemDetails_IfNoMappingWasFound()

View File

@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Fact]
public void InferBindingSourceForParameter_ReturnsPath_IfParameterAppearsInAnyRoutes_MulitpleRoutes()
public void InferBindingSourceForParameter_ReturnsPath_IfParameterAppearsInAnyRoutes_MultipleRoutes()
{
// Arrange
var actionName = nameof(ParameterBindingController.ParameterInMultipleRoutes);
@ -1112,7 +1112,7 @@ Environment.NewLine + "int b";
}
[Fact]
public void DiscoverErrorResponseType_UsesValueFromApiErrorTypeAttribute_SpecifiedOnControllerAsssembly()
public void DiscoverErrorResponseType_UsesValueFromApiErrorTypeAttribute_SpecifiedOnControllerAssembly()
{
// Arrange
var expected = typeof(InvalidEnumArgumentException);

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
@ -252,6 +253,33 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Equal(nameof(ConventionallyRoutedController.ConventionalAction), actionConstraint.Value);
}
[Fact]
public void GetDescriptors_EndpointMetadata_ContainsAttributesFromActionAndController()
{
// Arrange & Act
var descriptors = GetDescriptors(
typeof(AuthorizeController).GetTypeInfo());
// Assert
Assert.Equal(2, descriptors.Count());
var anonymousAction = Assert.Single(descriptors, a => a.RouteValues["action"] == "AllowAnonymousAction");
Assert.NotNull(anonymousAction.EndpointMetadata);
Assert.Collection(anonymousAction.EndpointMetadata,
metadata => Assert.IsType<AllowAnonymousAttribute>(metadata),
metadata => Assert.IsType<AuthorizeAttribute>(metadata));
var authorizeAction = Assert.Single(descriptors, a => a.RouteValues["action"] == "AuthorizeAction");
Assert.NotNull(authorizeAction.EndpointMetadata);
Assert.Collection(authorizeAction.EndpointMetadata,
metadata => Assert.Equal("ActionPolicy", Assert.IsType<AuthorizeAttribute>(metadata).Policy),
metadata => Assert.Equal("ControllerPolicy", Assert.IsType<AuthorizeAttribute>(metadata).Policy));
}
[Fact]
public void GetDescriptors_ActionWithHttpMethods_AddedToEndpointMetadata()
{
@ -272,7 +300,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.False(httpMethodMetadata.AcceptCorsPreflight);
Assert.Equal("GET", Assert.Single(httpMethodMetadata.HttpMethods));
});
},
metadata => Assert.IsType<RouteAttribute>(metadata));
}
[Fact]
@ -1175,7 +1204,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
[Theory]
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
public void ApiExplorer_ThrowsForContentionalRouting(string actionName, Type type)
public void ApiExplorer_ThrowsForConventionalRouting(string actionName, Type type)
{
// Arrange
var assemblyName = type.GetTypeInfo().Assembly.GetName().Name;
@ -1865,6 +1894,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void AttributeRoutedAction() { }
}
[Authorize("ControllerPolicy")]
private class AuthorizeController
{
[AllowAnonymous]
public void AllowAnonymousAction() { }
[Authorize("ActionPolicy")]
public void AuthorizeAction() { }
}
private class EmptyController
{
}

View File

@ -1206,7 +1206,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Theory]
[InlineData(nameof(TestController.AsynActionMethodWithTestActionResult))]
[InlineData(nameof(TestController.AsyncActionMethodWithTestActionResult))]
[InlineData(nameof(TestController.ActionMethodWithTestActionResult))]
public async Task InvokeAction_ReturnTypeAsIActionResult_ReturnsExpected(string methodName)
{
@ -1654,7 +1654,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return new TestActionResult { Value = value };
}
public async Task<TestActionResult> AsynActionMethodWithTestActionResult(int value)
public async Task<TestActionResult> AsyncActionMethodWithTestActionResult(int value)
{
return await Task.FromResult<TestActionResult>(new TestActionResult { Value = value });
}

View File

@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Fact]
public void EnumerateElements_TwoEnumerableImplemenations()
public void EnumerateElements_TwoEnumerableImplementations()
{
// Arrange
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });

View File

@ -1316,7 +1316,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
{ model, new ValidationStateEntry() }
};
var method = GetType().GetMethod(nameof(Validate_Throws_ForTopLeveleMetadataData), BindingFlags.NonPublic | BindingFlags.Instance);
var method = GetType().GetMethod(nameof(Validate_Throws_ForTopLevelMetadataData), BindingFlags.NonPublic | BindingFlags.Instance);
var metadata = MetadataProvider.GetMetadataForParameter(method.GetParameters()[0]);
// Act & Assert
@ -1465,7 +1465,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
void DoSomething();
}
private void Validate_Throws_ForTopLeveleMetadataData(DepthObject depthObject) { }
private void Validate_Throws_ForTopLevelMetadataData(DepthObject depthObject) { }
// Custom validation attribute that returns multiple entries in ValidationResult.MemberNames and those member
// names are indexers. An example scenario is an attribute that confirms all entries in a list are unique.

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
// Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
// Act
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true;
@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Equal($"The request body size limit has been disabled.", write.State.ToString());
}
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
{
return new AuthorizationFilterContext(CreateActionContext(), filters);
}

View File

@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Fact]
public void EnumerateElements_TwoEnumerableImplemenations()
public void EnumerateElements_TwoEnumerableImplementations()
{
// Arrange
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });

View File

@ -36,13 +36,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
// Arrange
Task requestDelegate(HttpContext context) => Task.FromResult(true);
var middlwareFilter = new MiddlewareFilter(requestDelegate);
var middlewareFilter = new MiddlewareFilter(requestDelegate);
var httpContext = new DefaultHttpContext();
var resourceExecutingContext = GetResourceExecutingContext(httpContext);
var resourceExecutionDelegate = GetResourceExecutionDelegate(httpContext);
// Act
await middlwareFilter.OnResourceExecutionAsync(resourceExecutingContext, resourceExecutionDelegate);
await middlewareFilter.OnResourceExecutionAsync(resourceExecutingContext, resourceExecutionDelegate);
// Assert
var feature = resourceExecutingContext.HttpContext.Features.Get<IMiddlewareFilterFeature>();
@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
logger,
diagnosticSource,
mapper,
CreatControllerContext(actionContext, valueProviderFactories, maxAllowedErrorsInModelState),
CreateControllerContext(actionContext, valueProviderFactories, maxAllowedErrorsInModelState),
CreateCacheEntry((ControllerActionDescriptor)actionContext.ActionDescriptor, controllerFactory),
filters)
{
@ -420,7 +420,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return ObjectMethodExecutor.Create(actionDescriptor.MethodInfo, actionDescriptor.ControllerTypeInfo);
}
private static ControllerContext CreatControllerContext(
private static ControllerContext CreateControllerContext(
ActionContext actionContext,
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
int maxAllowedErrorsInModelState)

View File

@ -112,9 +112,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var asyncResultFilter = Mock.Of<IAsyncResultFilter>();
var resourceFilter = Mock.Of<IResourceFilter>();
var asyncResourceFilter = Mock.Of<IAsyncResourceFilter>();
var orderedresourceFilterMock = new Mock<IOrderedResourceFilter>();
orderedresourceFilterMock.SetupGet(f => f.Order).Returns(-100);
var orderedResourceFilter = orderedresourceFilterMock.Object;
var orderedResourceFilterMock = new Mock<IOrderedResourceFilter>();
orderedResourceFilterMock.SetupGet(f => f.Order).Returns(-100);
var orderedResourceFilter = orderedResourceFilterMock.Object;
var filters = new IFilterMetadata[]
{
actionFilter,

View File

@ -377,7 +377,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressNameMetadata);
Assert.Equal(string.Empty, routeValuesAddressNameMetadata.Name);
Assert.Equal(string.Empty, routeValuesAddressNameMetadata.RouteName);
}
[Fact]
@ -402,7 +402,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressMetadata);
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
},
(ep) =>
@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressMetadata);
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
});
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Arrange
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
requestFormLimitsFilter.FormOptions = new FormOptions();
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestFormLimitsFilter });
// Set to null explicitly as we want to make sure the filter adds one
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Arrange
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
requestFormLimitsFilter.FormOptions = new FormOptions();
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestFormLimitsFilter });
var oldFormFeature = new FormFeature(authorizationFilterContext.HttpContext.Request);
// Set to null explicitly as we want to make sure the filter adds one
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
requestFormLimitsFilter.FormOptions = new FormOptions();
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestFormLimitsFilter });
authorizationFilterContext.HttpContext.Request.Form = new FormCollection(null);
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
requestFormLimitsFilter.FormOptions = new FormOptions();
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestFormLimitsFilter });
// Set to null explicitly as we want to make sure the filter adds one
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
requestFormLimitsFilter.FormOptions = new FormOptions();
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestFormLimitsFilter });
// Set to null explicitly as we want to make sure the filter adds one
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(
@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
write.State.ToString());
}
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
{
return new AuthorizationFilterContext(CreateActionContext(), filters);
}

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Arrange
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilter.Bytes = 12345;
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
requestSizeLimitResourceFilter.Bytes = 12345;
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilterFinal.Bytes = 0;
var authorizationFilterContext = CreateauthorizationFilterContext(
var authorizationFilterContext = CreateAuthorizationFilterContext(
new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
// Act
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true;
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString());
}
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
{
return new AuthorizationFilterContext(CreateActionContext(), filters);
}

View File

@ -467,7 +467,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[Theory]
[MemberData(nameof(DerivedInputFormattersThrowingNonInputFormatterException))]
public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormatingException_AddsErrorToModelState(
public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormattingException_AddsErrorToModelState(
IInputFormatter formatter,
string contentType,
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)

View File

@ -42,14 +42,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData(false, typeof(IntEnum))]
[InlineData(false, typeof(FlagsEnum))]
public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(
bool suprressBindingUndefinedValueToEnumType,
bool suppressBindingUndefinedValueToEnumType,
Type modelType)
{
// Arrange
var message = "The value '' is invalid.";
var binderInfo = GetBinderAndContext(
modelType,
suprressBindingUndefinedValueToEnumType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: "");
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{
[Theory]
[InlineData(typeof(FormCollection))]
[InlineData(typeof(DerviedFormCollection))]
[InlineData(typeof(DerivedFormCollection))]
public void Create_ThrowsException_ForFormCollectionModelType(Type modelType)
{
// Arrange
@ -62,9 +62,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{
}
private class DerviedFormCollection : FormCollection
private class DerivedFormCollection : FormCollection
{
public DerviedFormCollection() : base(fields: null, files: null) { }
public DerivedFormCollection() : base(fields: null, files: null) { }
}
}
}

View File

@ -607,7 +607,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
}
[Fact]
public async Task TryUpdataModel_ModelTypeDifferentFromModel_Throws()
public async Task TryUpdateModel_ModelTypeDifferentFromModel_Throws()
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();

View File

@ -21,10 +21,10 @@ namespace Microsoft.AspNetCore.Mvc
// Act & Assert
foreach (var property in formOptionsProperties)
{
var formLimiAttributeProperty = formLimitsAttributeProperties
var formLimitAttributeProperty = formLimitsAttributeProperties
.Where(pi => property.Name == pi.Name && pi.PropertyType == property.PropertyType)
.SingleOrDefault();
Assert.NotNull(formLimiAttributeProperty);
Assert.NotNull(formLimitAttributeProperty);
}
}

View File

@ -196,7 +196,7 @@ namespace Microsoft.AspNetCore.Mvc
[InlineData(null, false)]
[InlineData(true, false)]
[InlineData(false, true)]
public void OnAuthorization_RedirectsToHttpsEndpoint_WithSpecifiedStatusCodeAndrequireHttpsPermanentOption(bool? permanent, bool requireHttpsPermanent)
public void OnAuthorization_RedirectsToHttpsEndpoint_WithSpecifiedStatusCodeAndRequireHttpsPermanentOption(bool? permanent, bool requireHttpsPermanent)
{
var requestContext = new DefaultHttpContext();
requestContext.RequestServices = CreateServices(null, requireHttpsPermanent);

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
"testController",
"testAction");
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
var descriptorCollectionProvider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
var descriptorCollectionProvider = CreateActionDescriptorCollectionProvider(actionDescriptor);
var services = new ServiceCollection();
services.AddRouting();
@ -214,7 +214,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
"testAction");
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
var provider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
var provider = CreateActionDescriptorCollectionProvider(actionDescriptor);
var constraint = new KnownRouteValueConstraint(provider);
@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
private static HttpContext GetHttpContext(ActionDescriptor actionDescriptor, bool setupRequestServices = true)
{
var descriptorCollectionProvider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
var descriptorCollectionProvider = CreateActionDescriptorCollectionProvider(actionDescriptor);
var context = new Mock<HttpContext>();
if (setupRequestServices)
@ -247,7 +247,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return context.Object;
}
private static IActionDescriptorCollectionProvider CreateActionDesciprtorCollectionProvider(ActionDescriptor actionDescriptor)
private static IActionDescriptorCollectionProvider CreateActionDescriptorCollectionProvider(ActionDescriptor actionDescriptor)
{
var actionProvider = new Mock<IActionDescriptorProvider>(MockBehavior.Strict);

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