Skip to content

Commit ea1cb04

Browse files
committed
Merge branch 'merge/release/3.1-to-master'
2 parents 15e408d + 2c17931 commit ea1cb04

File tree

19 files changed

+140
-44
lines changed

19 files changed

+140
-44
lines changed

src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ This package is an internal implementation of the .NET Core SDK and is not meant
2525
<BaseRuntimeVersionFileName>aspnetcore_base_runtime.version</BaseRuntimeVersionFileName>
2626
<BaseRuntimeVersionFileOutputPath>$(InstallersOutputPath)$(BaseRuntimeVersionFileName)</BaseRuntimeVersionFileOutputPath>
2727

28-
<!-- NuGet appends target framework to this value. Example: runtimes/win-x64/lib/netcoreapp5.0/ -->
28+
<!-- NuGet appends target framework to this value. Example: runtimes/win-x64/lib/netcoreappX.Y/ -->
2929
<BuildOutputTargetFolder>runtimes/$(RuntimeIdentifier)/lib/</BuildOutputTargetFolder>
3030
<!-- We still need the native path to these assets though for the RuntimeList.xml manifest -->
3131
<ManagedAssetsPackagePath>$(BuildOutputTargetFolder)$(DefaultNetCoreTargetFramework)</ManagedAssetsPackagePath>

src/Mvc/Mvc.Analyzers/src/DiagnosticDescriptors.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public static class DiagnosticDescriptors
4747
new DiagnosticDescriptor(
4848
"MVC1004",
4949
"Rename model bound parameter.",
50-
"Property on type '{0}' has the same name as parameter '{1}'. This may result in incorrect model binding. Consider renaming the parameter or using a model binding attribute to override the name.",
50+
"Property on type '{0}' has the same name as parameter '{1}'. This may result in incorrect model binding. " +
51+
"Consider renaming the parameter or the property to avoid conflicts. If the type '{0}' has a custom type converter or custom model binder, you can suppress this message.",
5152
"Naming",
5253
DiagnosticSeverity.Warning,
5354
isEnabledByDefault: true,

src/Mvc/Mvc.Analyzers/src/TopLevelParameterNameAnalyzer.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,17 @@ internal static bool IsProblematicParameter(in SymbolCache symbolCache, IParamet
8989
return false;
9090
}
9191

92-
if (SpecifiesModelType(symbolCache, parameter))
92+
if (SpecifiesModelType(in symbolCache, parameter))
9393
{
9494
// Ignore parameters that specify a model type.
9595
return false;
9696
}
9797

98+
if (!IsComplexType(parameter.Type))
99+
{
100+
return false;
101+
}
102+
98103
var parameterName = GetName(symbolCache, parameter);
99104

100105
var type = parameter.Type;
@@ -122,6 +127,26 @@ internal static bool IsProblematicParameter(in SymbolCache symbolCache, IParamet
122127
return false;
123128
}
124129

130+
private static bool IsComplexType(ITypeSymbol type)
131+
{
132+
// This analyzer should not apply to simple types. In MVC, a simple type is any type that has a type converter that returns true for TypeConverter.CanConvertFrom(typeof(string)).
133+
// Unfortunately there isn't a Roslyn way of determining if a TypeConverter exists for a given symbol or if the converter allows string conversions.
134+
// https://github.com/dotnet/corefx/blob/v3.0.0-preview8.19405.3/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs#L103-L141
135+
// provides a list of types that have built-in converters.
136+
// We'll use a simpler heuristic in the analyzer: A type is simple if it's a value type or if it's in the "System.*" namespace hierarchy.
137+
138+
var @namespace = type.ContainingNamespace?.ToString();
139+
if (@namespace != null)
140+
{
141+
// Things in the System.* namespace hierarchy don't count as complex types. This workarounds
142+
// the problem of discovering type converters on types in mscorlib.
143+
return @namespace != "System" &&
144+
!@namespace.StartsWith("System.", StringComparison.Ordinal);
145+
}
146+
147+
return true;
148+
}
149+
125150
internal static string GetName(in SymbolCache symbolCache, ISymbol symbol)
126151
{
127152
foreach (var attribute in symbol.GetAttributes(symbolCache.IModelNameProvider))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace Microsoft.AspNetCore.Mvc.Analyzers.TopLevelParameterNameAnalyzerTestFiles
4+
{
5+
public class IsProblematicParameter_ReturnsFalse_ForSimpleTypes
6+
{
7+
public void ActionMethod(DateTime date, DateTime? day, Uri absoluteUri, Version majorRevision, DayOfWeek sunday) { }
8+
}
9+
}

src/Mvc/Mvc.Analyzers/test/TopLevelParameterNameAnalyzerTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,30 @@ public async Task IsProblematicParameter_ReturnsFalse_ForParametersWithCustomMod
9595
Assert.False(result);
9696
}
9797

98+
// Test for https://github.com/aspnet/AspNetCore/issues/6945
99+
[Fact]
100+
public async Task IsProblematicParameter_ReturnsFalse_ForSimpleTypes()
101+
{
102+
var testName = nameof(IsProblematicParameter_ReturnsFalse_ForSimpleTypes);
103+
var testSource = MvcTestSource.Read(GetType().Name, testName);
104+
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
105+
106+
var compilation = await project.GetCompilationAsync();
107+
108+
var modelType = compilation.GetTypeByMetadataName($"Microsoft.AspNetCore.Mvc.Analyzers.TopLevelParameterNameAnalyzerTestFiles.{testName}");
109+
var method = (IMethodSymbol)modelType.GetMembers("ActionMethod").First();
110+
111+
Assert.True(TopLevelParameterNameAnalyzer.SymbolCache.TryCreate(compilation, out var symbolCache));
112+
113+
Assert.Collection(
114+
method.Parameters,
115+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
116+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
117+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
118+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
119+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)));
120+
}
121+
98122
[Fact]
99123
public async Task IsProblematicParameter_IgnoresStaticProperties()
100124
{

src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
1111
</li>
1212
<li class="nav-item">
13-
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/LogOut">Logout</a>
13+
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
14+
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
15+
</form>
1416
</li>
1517
}
1618
else

src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
],
99
"name": "ASP.NET Core Web App",
1010
"generatorVersions": "[1.0.0.0-*)",
11-
"description": "A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content",
11+
"description": "A project template for creating an ASP.NET Core application with example ASP.NET Core Razor Pages content",
1212
"groupIdentity": "Microsoft.Web.RazorPages",
1313
"precedence": "5000",
1414
"identity": "Microsoft.Web.RazorPages.CSharp.3.0",

src/ProjectTemplates/test/BlazorServerTemplateTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public async Task BlazorServerTemplateWorks_NoAuth()
3636
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
3737

3838
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
39-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
39+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
4040
// later, while the opposite is not true.
4141

4242
var buildResult = await Project.RunDotNetBuildAsync();
@@ -93,7 +93,7 @@ public async Task BlazorServerTemplateWorks_IndividualAuth(bool useLocalDB)
9393
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
9494

9595
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
96-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
96+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
9797
// later, while the opposite is not true.
9898

9999
var buildResult = await Project.RunDotNetBuildAsync();

src/ProjectTemplates/test/EmptyWebTemplateTest.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,19 @@ public EmptyWebTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHel
2222

2323
public ITestOutputHelper Output { get; }
2424

25-
[Theory]
26-
[InlineData(null)]
27-
[InlineData("F#")]
28-
public async Task EmptyWebTemplateAsync(string languageOverride)
25+
[Fact]
26+
public async Task EmptyWebTemplateCSharp()
27+
{
28+
await EmtpyTemplateCore(languageOverride: null);
29+
}
30+
31+
[Fact]
32+
public async Task EmptyWebTemplateFSharp()
33+
{
34+
await EmtpyTemplateCore("F#");
35+
}
36+
37+
private async Task EmtpyTemplateCore(string languageOverride)
2938
{
3039
Project = await ProjectFactory.GetOrCreateProject("empty" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
3140

@@ -42,7 +51,7 @@ public async Task EmptyWebTemplateAsync(string languageOverride)
4251
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
4352

4453
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
45-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
54+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
4655
// later, while the opposite is not true.
4756

4857
var buildResult = await Project.RunDotNetBuildAsync();

src/ProjectTemplates/test/Helpers/AspNetProcess.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public AspNetProcess(
5858

5959
var arguments = published ? $"exec {dllPath}" : "run";
6060
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables);
61-
if(hasListeningUri)
61+
if (hasListeningUri)
6262
{
6363
ListeningUri = GetListeningUri(output);
6464
}
@@ -108,7 +108,7 @@ public async Task ContainsLinks(Page page)
108108
HttpMethod.Get,
109109
new Uri(ListeningUri, page.Url));
110110

111-
var response = await _httpClient.SendAsync(request);
111+
var response = await RequestWithRetries(client => client.SendAsync(request), _httpClient);
112112

113113
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
114114
var parser = new HtmlParser();
@@ -141,14 +141,36 @@ public async Task ContainsLinks(Page page)
141141
Assert.True(string.Equals(anchor.Href, expectedLink), $"Expected next link to be {expectedLink} but it was {anchor.Href}.");
142142
var result = await RetryHelper.RetryRequest(async () =>
143143
{
144-
return await _httpClient.GetAsync(anchor.Href);
144+
return await RequestWithRetries(client => client.GetAsync(anchor.Href), _httpClient);
145145
}, logger: NullLogger.Instance);
146146

147147
Assert.True(IsSuccessStatusCode(result), $"{anchor.Href} is a broken link!");
148148
}
149149
}
150150
}
151151

152+
private async Task<T> RequestWithRetries<T>(Func<HttpClient, Task<T>> requester, HttpClient client, int retries = 3, TimeSpan initialDelay = default)
153+
{
154+
var currentDelay = initialDelay == default ? TimeSpan.FromSeconds(30) : initialDelay;
155+
for (int i = 0; i <= retries; i++)
156+
{
157+
try
158+
{
159+
return await requester(client);
160+
}
161+
catch (Exception)
162+
{
163+
if (i == retries)
164+
{
165+
throw;
166+
}
167+
await Task.Delay(currentDelay);
168+
currentDelay *= 2;
169+
}
170+
}
171+
throw new InvalidOperationException("Max retries reached.");
172+
}
173+
152174
private Uri GetListeningUri(ITestOutputHelper output)
153175
{
154176
// Wait until the app is accepting HTTP requests
@@ -190,7 +212,7 @@ public Task AssertNotFound(string requestUrl)
190212

191213
internal Task<HttpResponseMessage> SendRequest(string path)
192214
{
193-
return _httpClient.GetAsync(new Uri(ListeningUri, path));
215+
return RequestWithRetries(client => client.GetAsync(new Uri(ListeningUri, path)), _httpClient);
194216
}
195217

196218
public async Task AssertStatusCode(string requestUrl, HttpStatusCode statusCode, string acceptContentType = null)
@@ -204,7 +226,7 @@ public async Task AssertStatusCode(string requestUrl, HttpStatusCode statusCode,
204226
request.Headers.Add("Accept", acceptContentType);
205227
}
206228

207-
var response = await _httpClient.SendAsync(request);
229+
var response = await RequestWithRetries(client => client.SendAsync(request), _httpClient);
208230
Assert.True(statusCode == response.StatusCode, $"Expected {requestUrl} to have status '{statusCode}' but it was '{response.StatusCode}'.");
209231
}
210232

src/ProjectTemplates/test/IdentityUIPackageTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public async Task IdentityUIPackage_WorksWithDifferentOptions(IDictionary<string
134134
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
135135

136136
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
137-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
137+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
138138
// later, while the opposite is not true.
139139

140140
var buildResult = await Project.RunDotNetBuildAsync(packageOptions: packageOptions);

src/ProjectTemplates/test/MvcTemplateTest.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ public MvcTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper o
2525
public ProjectFactoryFixture ProjectFactory { get; }
2626
public ITestOutputHelper Output { get; }
2727

28-
[Theory]
29-
[InlineData(null)]
30-
[InlineData("F#")]
31-
[Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2267", FlakyOn.All)]
32-
public async Task MvcTemplate_NoAuthImplAsync(string languageOverride)
28+
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/14022")]
29+
public async Task MvcTemplate_NoAuthFSharp() => await MvcTemplateCore(languageOverride: "F#");
30+
31+
[Fact]
32+
public async Task MvcTemplate_NoAuthCSharp() => await MvcTemplateCore(languageOverride: null);
33+
34+
35+
private async Task MvcTemplateCore(string languageOverride)
3336
{
3437
Project = await ProjectFactory.GetOrCreateProject("mvcnoauth" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
3538

@@ -54,7 +57,7 @@ public async Task MvcTemplate_NoAuthImplAsync(string languageOverride)
5457
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
5558

5659
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
57-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
60+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
5861
// later, while the opposite is not true.
5962

6063
var buildResult = await Project.RunDotNetBuildAsync();
@@ -104,8 +107,7 @@ public async Task MvcTemplate_NoAuthImplAsync(string languageOverride)
104107
[Theory]
105108
[InlineData(true)]
106109
[InlineData(false)]
107-
[Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2267", FlakyOn.All)]
108-
public async Task MvcTemplate_IndividualAuthImplAsync(bool useLocalDB)
110+
public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
109111
{
110112
Project = await ProjectFactory.GetOrCreateProject("mvcindividual" + (useLocalDB ? "uld" : ""), Output);
111113

@@ -122,7 +124,7 @@ public async Task MvcTemplate_IndividualAuthImplAsync(bool useLocalDB)
122124
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
123125

124126
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
125-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
127+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
126128
// later, while the opposite is not true.
127129

128130
var buildResult = await Project.RunDotNetBuildAsync();

src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public async Task RazorClassLibraryTemplate_WithViews_Async()
3333
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
3434

3535
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
36-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
36+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
3737
// later, while the opposite is not true.
3838

3939
var buildResult = await Project.RunDotNetBuildAsync();
@@ -52,7 +52,7 @@ public async Task RazorClassLibraryTemplateAsync()
5252
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
5353

5454
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
55-
// The output from publish will go into bin/Release/netcoreapp5.0/publish and won't be affected by calling build
55+
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
5656
// later, while the opposite is not true.
5757

5858
var buildResult = await Project.RunDotNetBuildAsync();

0 commit comments

Comments
 (0)